from os import environ
from glob import glob

def cppfiles(s):
    return map("%s.c++".__mod__, s.split())

def toflags(prefix, stuff):
    return set(map(prefix.__add__, stuff))

def flaglist(*args):
    return sorted(reduce(set.union, args, set()))

def flagstr(*args):
    return " ".join(flaglist(*args))

def filterenv(env):
    valid = "CC CFLAGS CXX CXXFLAGS LINK LINKFLAGS".split()
    return dict([(k, v) for (k, v) in env.items() if k in valid])

ctags = Builder(action=
                "ctags -f tags --extra=+q --fields=+i --c++-kinds=+p $SOURCES")

platform = str(Platform()) # know values: posix, win32

# known values: g++, cl
compiler = Environment(ENV=environ, **filterenv(environ)).subst('$CXX')

def addifgnu(s, *options):
    global compiler
    if compiler.startswith('g++'):
        s.update(options)
def addifmsvc(s, *options):
    global compiler
    if compiler == 'cl':
        s.update(options)

opts = Options()
opts.Add(BoolOption('WITH_EPOLL', 'Build with epoll', platform == 'posix'))
opts.Add(BoolOption('WITH_CTAGS', 'Build ctags tag file', platform == 'posix'))
opts.Add(BoolOption('WITH_DOXYGEN', 'Build doxygen html documents',
                platform == 'posix'))
opts.Add(BoolOption('WITH_GMP', 'Build with gmp', platform == 'posix'))
opts.Add(BoolOption('WITH_MUDFLAP', 'Build with mudflap', 0))
opts.Add(BoolOption('WITH_NOEXITLEAK', 'Do not leak memory on exit', 0))
opts.Add(BoolOption('WITH_OPTIMIZE', 'Build with optimization', 0))
opts.Add(BoolOption('WITH_PROFILE', 'Build with profiling', 0))
opts.Add(BoolOption('WITH_PTHREAD', 'Build with pthread', platform == 'posix'))
opts.Add(BoolOption('WITH_SELECT', 'Build with select', 1))
opts.Add(BoolOption('WITH_SYSCONF_CPUS', 'Build with sysconf',
                platform == 'posix'))
opts.Add(BoolOption('WITH_VALGRIND', 'Build for valgrind', 0))
opts.Add(BoolOption('WITH_WIN32', 'Build for win32', platform == 'win32'))
env = Environment(options=opts, ENV=environ, **filterenv(environ))
env.Append(BUILDERS=dict(CTags=ctags))
Help(opts.GenerateHelpText(env))

libmuth = cppfiles("""basescheduler callback channel coroutine cpudetect
                currentthread globalqueue microthread runner waitqueue""")
epoll = cppfiles("epollscheduler")
pthread = cppfiles("pthreadcondition pthreadlock")
select = cppfiles("selectscheduler selectwrapper")
unittest = cppfiles("""unittest unittest_main blockingqueue_test refcounter_test
		timeval_test""")
win32 = cppfiles("win32")

flags = set()
addifgnu(flags, *"""-W -Wall -pedantic -Wundef -Wendif-labels -Wshadow
        -Wpointer-arith -Wcast-align -Wwrite-strings -Winline
        -Wdisabled-optimization -ggdb3""".split())
macros = set()
includes = set()
libs = set()

lib = list(libmuth)

if env["WITH_EPOLL"] and compiler == 'g++':
    lib += epoll
    macros.add("USE_EPOLL")

if env["WITH_OPTIMIZE"]:
    macros.add("NDEBUG")
    addifgnu(flags, "-O3")
    if not env["WITH_PROFILE"]:
        addifgnu(flags, "-ffunction-sections", "-fdata-sections")

if env["WITH_MUDFLAP"]:
    if env["WITH_PTHREAD"]:
        addifgnu(flags, "-fmudflapth")
    else:
        addifgnu(flags, "-fmudflap")

if env["WITH_NOEXITLEAK"]:
    macros.add("NOEXITLEAK")

if env["WITH_PROFILE"]:
    addifgnu(flags, "-pg")

if env["WITH_PTHREAD"] and platform == 'posix':
    lib += pthread
    macros.add("USE_PTHREAD")
    flags.add("-pthread")
    libs.add("pthread")
    if env["WITH_SYSCONF_CPUS"]:
        macros.add("USE_SYSCONF_CPUS")

if env["WITH_SELECT"]:
    lib += select

if env["WITH_VALGRIND"]:
    macros.add("USE_VALGRIND")
    includes.add("/usr/include/valgrind")

if env["WITH_WIN32"]:
    macros.add("__WIN32__")
    macros.add("NOMINMAX")
    lib += win32

env.Append(CXXFLAGS=" " + flagstr(flags, toflags("-D", macros)))
includes = flaglist(includes)
libs.add("libmuth")
libs = flaglist(libs)

env.Library("libmuth", lib, CPPPATH=includes)
env.Library("unittest", unittest, CPPPATH=includes)
env.Program("test", ["test.c++"], LIBS=libs, LIBPATH=".", CPPPATH=includes)
if platform == 'posix':
    env.Program("testweb", ["testweb.c++"], LIBS=libs, LIBPATH=".",
                CPPPATH=includes)
    env.Program("testforward", ["testforward.c++"], LIBS=libs, LIBPATH=".",
                CPPPATH=includes)
if env["WITH_GMP"]:
    env.Program("testprime", ["testprime.c++"], LIBS=libs+["gmp"], LIBPATH=".",
                CPPPATH=includes)
env.Program("unittest_simple", ["unittest_simple.c++"], LIBS=["unittest"]+libs,
                LIBPATH=".", CPPPATH=includes)
env.Program("unittest_libmuth", ["unittest_libmuth.c++"],
                LIBS=libs+["unittest"], LIBPATH=".", CPPPATH=includes)
if env["WITH_DOXYGEN"]:
    h = env.Command("html", ["Doxyfile"] + glob("*.h"), "doxygen")
    Clean(h, "html")
if env["WITH_CTAGS"]:
    env.CTags("tags", glob("*.[ch]*"))

# vim:syntax=python
# vim7:nospell
