#!/usr/bin/env python3.1

import os
import pprint
import sys

# Make sure the current working directory is in the search path so we can find
# the fbuildroot.py.
sys.path.append(os.getcwd())

import fbuild
import fbuild.db
import fbuild.target
import fbuild.path
import fbuild.context
import fbuild.options

# If we can't import fbuildroot, save the exception and raise it later.
try:
    import fbuildroot
except ImportError as e:
    fbuildroot = e

# ------------------------------------------------------------------------------

def parse_args(argv):
    parser = fbuild.options.make_parser()

    # -------------------------------------------------------------------------
    # Let the fbuildroot modify the optparse parser before parsing.

    try:
        pre_options = fbuildroot.pre_options
    except AttributeError:
        pass
    else:
        parser = pre_options(parser) or parser

    options, args = parser.parse_args(argv[1:])

    # -------------------------------------------------------------------------
    # Let the fbuildroot modify the optparse parser after parsing.

    try:
        post_options = fbuildroot.post_options
    except AttributeError:
        pass
    else:
        options, args = post_options(options, args) or (options, args)

    return fbuild.context.Context(options, args)

# ------------------------------------------------------------------------------

def build(ctx):
    # Exit early if we're just viewing the state.
    if ctx.options.dump_state:
        # print out the entire state
        pprint.pprint(fbuild.db.database.__dict__)
        return 0

    # Exit early if we're just clearing a function.
    if ctx.options.clear_function:
        if not ctx.db.clear_function(options.clear_function):
            raise fbuild.Error('function %r not cached' %
                    ctx.options.clear_function)
        return 0

    # Exit early if we're just clearing a file.
    if ctx.options.clear_file:
        if not ctx.db.clear_file(options.clear_file):
            raise fbuild.Error('file %r not cached' % ctx.options.clear_file)
        return 0

    # We'll use the arguments as our targets.
    targets = ctx.args or ['build']

    # Step through each target and execute it.
    for target_name in targets:
        target = fbuild.target.find(target_name)

        target.function(ctx)

    return 0

# ------------------------------------------------------------------------------

def main(argv=None):
    # Register a couple functions as targets.
    for name in ('configure', 'build'):
        try:
            fbuild.target.register(name=name)(getattr(fbuildroot, name))
        except AttributeError:
            pass

    # --------------------------------------------------------------------------

    ctx = parse_args(sys.argv if argv is None else argv)

    # If the fbuildroot doesn't exist, error out. We do this now so that
    # there's a chance to ask fbuild for help first.
    if isinstance(fbuildroot, Exception):
        raise fbuildroot

    # Exit early if we want to clean the buildroot.
    if ctx.options.clean_buildroot:
        # Only try to clean the buildroot if it actually exists.
        if ctx.options.buildroot.exists():
            ctx.options.buildroot.rmtree()
        return

    # Prep the context for running.
    ctx.create_buildroot()
    ctx.load_configuration()

    # ... and then run the build.
    try:
        result = build(ctx)
    except fbuild.Error as e:
        ctx.logger.log(e, color='red')
        return 1
    except KeyboardInterrupt:
        # It appears that we can't reliably shutdown the scheduler's threads
        # when SIGINT is emitted, because python may raise KeyboardInterrupt
        # between the finally and the mutex.release call.  So, we can find
        # ourselves exiting functions with the lock still held.  This could
        # then cause deadlocks if that lock was ever acquired again.  Oiy.
        raise
    else:
        # No exception occurred, so let us be good and shut down the scheduler.
        ctx.shutdown()
    finally:
        ctx.save_configuration()

    return result

# ------------------------------------------------------------------------------

if __name__ == '__main__':
    sys.exit(main())
