937a200089
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.
507 lines
14 KiB
Python
Executable File
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
|
|
###
|