# vim: set fileencoding=utf-8
"""contrib.setup.config module.

Improved config command, in order to support the traditional
configure + make pattern, e.g.

    $ python setup.py config --pg-config=/usr/local/psql/bin/pg_config
    $ python setup.py build

THIS SOFTWARE IS UNDER MIT LICENSE.
Copyright (c) 2013 Perillo Manlio (manlio.perillo@gmail.com)

Read LICENSE file for more informations.
"""

import os.path
import cPickle as pickle
from distutils import dep_util
from distutils.command.config import config as _config


__all__ = ["config"]


class config(_config):
    """Improved config command, in order to support the traditional
    configure + make pattern.

    When the command is first (or explicitly) executed, the configure
    method is called and options are saved to a config file, adjacent to
    the setup.py script.  Successive command invocations will read
    options from the config file.

    This allows the configuration to be run as a separate step, allowing
    the user to override configuration variables using the command line,
    e.g.:

        $ python setup.py config --pg-config=pg_config
        $ python setup.py build

    Note that distutils allows, e.g.

        $ python setup.py config --pg-config=pg_config build

    However this is tedious, if you need to run the build command
    multiple time (e.g. during development).

    It is also possible to modify the setup.cfg file, but allowing
    options to be configured using command line is a usually a better
    design.
    """

    def __getstate__(self):
        """Skip all the callable instance attributes, and the
        distribution attribute.
        """

        state = {}
        for name, value in self.__dict__.iteritems():
            # XXX check me, maybe we just need to skip non pickleable
            # objects
            if not callable(value) and name != "distribution":
                state[name] = value

        return state


    def run(self):
        """If a config.dat file exists, load options from that file.
        If the file does not exists, call the configure method and
        save the options to the config.dat file.

        However always call the configure method, if the config command
        was explicitly executed or setup.cfg file has been modified.

        Current implementation serialize options (as a dictionary) using
        pickle format, but JSON may be a better choice.
        """

        # Make sure to run configure again, if user requested it
        # (since the user may specify different options)
        explicit = "config" in self.distribution.commands

        # And make sure to run it again if setup.cfg has been modified
        # (since the user may change the [config] entries)
        newer = dep_util.newer("setup.cfg", "config.dat")

        if os.path.exists("config.dat") and not explicit and not newer:
            fp = open("config.dat", "rb")
            try:
                options = pickle.load(fp)
            finally:
                fp.close()

            # XXX check me
            self.__dict__.update(options)
        else:
            self.configure()

            # XXX check me
            options = self.__getstate__()

            fp = open("config.dat", "wb")
            try:
                pickle.dump(options, fp)
            except:
                # Don't leave the config.dat file around
                os.remove("config.dat")

                # TODO: error handling
                raise
            finally:
                fp.close()
