diff options
Diffstat (limited to 'serfmake')
-rwxr-xr-x | serfmake | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/serfmake b/serfmake new file mode 100755 index 0000000..3ced654 --- /dev/null +++ b/serfmake @@ -0,0 +1,485 @@ +#!/usr/bin/env python + +import os +import re +import shutil +import sys +import stat +import copy + +### use get_version() ? +MAJOR = 1 + +# Basic defines for our outputs. +LIBNAME = 'libserf-%d' % (MAJOR,) +INCLUDES = 'serf-%d' % (MAJOR,) +PCFILE = 'serf-%d' % (MAJOR,) + + +FILES_HDR = [ + ('.', 'serf'), + ('.', 'serf_bucket_types'), + ('.', 'serf_bucket_util'), + ] + +LIB_FILES = [ + ('.', 'context'), + ('.', 'incoming'), + ('.', 'outgoing'), + ('.', 'ssltunnel'), + + ('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'), + ('buckets', 'iovec_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/server', 'test_server'), + ] + +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('.', LIBNAME, 'la'), False) + builder.build_target(File('.', PCFILE, 'pc'), False) + + +def cmd_install(param): + builder = Builder(param) + ### should be called .install_all() + builder.install_target(File('.', LIBNAME, '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('.', LIBNAME, 'la'), + File('.', PCFILE, 'pc'), + ] + 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(object): + 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.prefix = params['prefix'] + except: + self.prefix = '/usr/local' + + ### no way to tweak these + self.libdir = os.path.join(self.prefix, 'lib') + self.pkgconfigdir = os.path.join(self.prefix, 'lib', 'pkgconfig') + self.includedir = os.path.join(self.prefix, 'include', INCLUDES) + + 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' + self.SSL_LIBS = '-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 + + all_libs = self.LIBS + ' ' + self.SSL_LIBS + + lib = File('.', LIBNAME, '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]), all_libs) + self._add_dep(lib, libobjs, cmd) + + # load the test program dependencies now + testhdrs = copy.deepcopy(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]), + all_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, all_libs) + self._add_dep(prog, [lib, obj], cmd) + + # create 'serf-1.pc' if it doesn't exist. + pcfile = File('.', PCFILE, 'pc') + self._add_dep(pcfile, [], self._write_pcfile) + + 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. + + # Commands that are strings are cmdline invocations. Otherwise, it + # should be a callable. + if isinstance(cmd, str): + cmd = CommandLine(cmd) + + # register the dependency so this will get built + self.deps[target] = deps, cmd + + def _write_pcfile(self): + """Generating serf-1.pc ...""" + + open(PCFILE + '.pc', 'w').write( +"""SERF_MAJOR_VERSION=%d +prefix=%s +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/%s + +Name: serf +Description: HTTP client library +Version: %s +Requires.private: libssl libcrypto +Libs: -L${libdir} -lserf-${SERF_MAJOR_VERSION} +Libs.private: %s +Cflags: -I${includedir} +""" % (MAJOR, self.prefix, INCLUDES, get_version(), self.LIBS)) + + def build_target(self, target, dry_run): + deps, cmd = self.deps.get(target, (None, None)) + if cmd is None: + # it's already up to date. all done. + return + + for f in deps: + subdep = self.deps.get(f) + if subdep: + self.build_target(f, dry_run) + + # build the target now + print(cmd.__doc__) + if not dry_run: + result = cmd() + if result: + raise BuildError(cmd.__doc__, 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: + + for path in (self.libdir, self.pkgconfigdir, self.includedir): + if not os.path.exists(path): + try: + os.makedirs(path) + except OSError: + raise BuildError('os.makedirs', + 'can not create install directories') + + for f in self.hdrs: + print("Installing: %s" % (os.path.basename(f.fname),)) + shutil.copy(f.fname, self.includedir) + + print("Installing: %s.pc" % (PCFILE,)) + shutil.copy(PCFILE + '.pc', self.pkgconfigdir) + + cmd = '%s --silent --mode=install %s -c -m %d %s %s' % ( + self.LIBTOOL, '/usr/bin/install', self.MODE, target.fname, + self.libdir) + + print("Installing: %s" % (os.path.basename(target.fname),)) + 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 CommandLine(object): + """Simple helper to invoke a system command when called.""" + + def __init__(self, cmd): + self.cmd = cmd + self.__doc__ = cmd # when we print the execution of this command + + def __call__(self): + return os.system(self.cmd) + + +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) + + +def get_version(): + match = re.search('SERF_MAJOR_VERSION ([0-9]+).*' + 'SERF_MINOR_VERSION ([0-9]+).*' + 'SERF_PATCH_VERSION ([0-9]+)', + open('serf.h').read(), + re.DOTALL) + major, minor, patch = match.groups() + return '%s.%s.%s' % (major, minor, patch) + + +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 +### |