#!/usr/bin/env python
import distutils.core, Cython.Build, Cython.Build.Dependencies, importlib, os, os.path, sys, socket
import multiprocessing, distutils.sysconfig, errno, shutil, tempfile, re, platform
if len(sys.argv) <= 1: raise Exception("Usage: "+sys.argv[0]+" cython-script.pyx")
# determine the build path for the cached extension library
build_dir = os.path.join(
    os.environ.get("CYTHON_BIN", 
        ".cython" if os.path.exists(".cython") else os.path.expanduser("~/.cache/cython")),
    re.sub('/', '%2F', re.sub('%', '%%', 
        "/".join([socket.gethostname(), platform.python_version(), os.path.abspath(sys.argv[1])]))))
# get the module name
module = os.path.splitext(os.path.basename(sys.argv[1]))[0]
# read the include_path parameters
conf = {'realpath': [True], 'base': None, 'ignore': []}
include_path = ['.']
with open(sys.argv[1]) as f:
    for line in f:
        if not re.search(r'^#', line): break
        m = re.search(r'^#\s*include_path\s*:([^=]+)=(.*)$', line)
        if m: conf[m.group(1).strip()] = Cython.Build.Dependencies.parse_list(m.group(2).strip())
# find the include_path location if base is specified
if conf['base'] is not None and len(conf['base'])>0:
    path = os.path.dirname(os.path.realpath(sys.argv[1]) if conf['realpath'][0] else os.path.abspath(sys.argv[1]))
    while os.path.dirname(path) != path and (path in conf['ignore'] or not os.path.isdir(os.path.join(path, conf['base'][0]))):
        path = os.path.dirname(path)
    if path not in conf['ignore'] and os.path.isdir(os.path.join(path, conf['base'][0])):
        include_path.insert(0, os.path.join(path, conf['base'][0]))

# create the build path if it doesn't already exist
try: 
    os.makedirs(build_dir, 0o700)
except OSError as e: 
    if e.errno != errno.EEXIST: 
        raise
# create the temp build directory as a subdirectory of the build path
tmpdir = tempfile.mkdtemp(dir=build_dir)
try:
    # make empty files in the temp build directory with the correct mtime to avoid
    # rebuilding every time
    for f in os.listdir(build_dir): 
        if os.path.isfile(os.path.join(build_dir,f)):
            mtime = os.path.getmtime(os.path.join(build_dir, f))
            with open(os.path.join(tmpdir, f), "a"): 
                os.utime(os.path.join(tmpdir, f), (mtime, mtime))
    # remove spurious CFLAGS added by distutils
    config = distutils.sysconfig.get_config_vars()
    config["CFLAGS"] = ""
    # run distutils build_ext to build the extension module
    distutils.core.setup(ext_modules=Cython.Build.cythonize(distutils.core.Extension( 
                module, sources=[sys.argv[1]]), 
            build_dir=tmpdir,
            quiet='VERBOSE' not in os.environ,
            nthreads=multiprocessing.cpu_count(),
            include_path=include_path,
            force='FORCE' in os.environ), 
        script_args=["-v" if 'VERBOSE' in os.environ else "-q", "build_ext", "-b",tmpdir, "-t","/"])
    # move any updated files back to the build path
    for f in os.listdir(tmpdir):
        if not os.path.exists(os.path.join(build_dir, f)) or (os.path.getmtime(os.path.join(build_dir, f)) < os.path.getmtime(os.path.join(tmpdir, f))):
            os.rename(os.path.join(tmpdir, f), os.path.join(build_dir, f))
# remove the temporary build directory
finally: 
    shutil.rmtree(tmpdir)
# add the build path to the module load path and run the extension module
if not "CHECK" in os.environ:
    sys.argv.pop(0)
    sys.path.insert(1, os.path.abspath(build_dir))
    importlib.import_module(module)
sys.exit(0)
