freebsd-dev/contrib/serf/serfmake
Peter Wemm 937a200089 Introduce svnlite so that we can check out our source code again.
This is actually a fully functional build except:
* All internal shared libraries are static linked to make sure there
  is no interference with ports (and to reduce build time).
* It does not have the python/perl/etc plugin or API support.
* By default, it installs as "svnlite" rather than "svn".
* If WITH_SVN added in make.conf, you get "svn".
* If WITHOUT_SVNLITE is in make.conf, this is completely disabled.

To be absolutely clear, this is not intended for any use other than
checking out freebsd source and committing, like we once did with cvs.

It should be usable for small scale local repositories that don't
need the python/perl plugin architecture.
2013-06-18 02:53:45 +00:00

507 lines
14 KiB
Python
Executable File

#!/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', 'response_body_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/server', 'test_sslserver'),
]
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\t\tprefix for installed APR and APR-util')
print('\t\t\t\t(needs apr-1-config and apu-1-config; will look in PATH)')
print('\t--with-gssapi=PATH\tbuild serf with GSSAPI support')
print('\t\t\t\t(needs krb5-config; will look in PATH/bin)')
print('\t--prefix=PATH\t\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)
builder.build_target(File('.', PCFILE, 'pc'), False)
### 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):
# use apr option if set
if 'apr' in params:
self.apr = APRConfig(params['apr'])
self.apu = APUConfig(params['apr'])
else:
self.apr = APRConfig(None)
self.apu = APUConfig(None)
# build with gssapi if option is set
if 'gssapi' in params:
self.gssapi = GSSAPIConfig(params['gssapi'])
else:
self.gssapi = 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'
if self.gssapi:
self.LIBS += ' ' + self.gssapi.get_value(None, '--libs gssapi')
self.CFLAGS += ' ' + self.gssapi.get_value('CFLAGS', '--cflags gssapi')\
+ ' -DSERF_HAVE_GSSAPI -g'
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 GSSAPIConfig(ConfigScript):
script_name = 'krb5-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
###