# Copyright (c) 2001, 2002, 2003 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""Distutils setup script for Thuban."""

__version__ = "$Revision: 1.42 $"

# Configuration:
#
# depending on your platform you may have to configure some variables by
# hand below.
#

import os
from types import TupleType
import glob
from distutils.core import setup, Extension, Command
from distutils.command.install import install, INSTALL_SCHEMES, subst_vars
from distutils.command.build_py import build_py
from distutils.command.bdist_rpm import bdist_rpm
from distutils.command.build_ext import build_ext
from distutils.file_util import write_file
from distutils.filelist import FileList
from distutils.util import convert_path, change_root

from distutils import archive_util, dir_util
import distutils

from string import split
import string

# config script parameter list indices
CS_DEFS, CS_INCDIRS, CS_LIBDIRS, CS_LIBS, CS_NUM_PARAMS = range(5)

# support for gdal is on by default, but under posix we try to
# detect it anyway. Set this to False to disable GDAL.
include_gdal = True

if os.name == "posix":
    ###################################################################
    # Posix configuration. Adapt this if you're running some kind of
    # Unix like system.
   
    # Directories where Proj4 is installed
    proj4_prefix = "/usr/local/"
    proj4_incdir =  os.path.join(proj4_prefix, "include")
    proj4_libdir =  os.path.join(proj4_prefix, "lib")
    proj4_lib = "proj"


    # You shouldn't have to modify anything below here
    ###################################################################
    
    # The installation prefix (similar to autoconf's --prefix). This is
    # only the default value, you can override it on the command line
    # with the install command's --prefix option.
    # 
    # Note that there's a separate prefix option for the bdist_rpm
    # command completely independent of this one.
    prefix = "/usr/local/"

    # Whether to create the thubaninit module. You can override this
    # value on the commandline with the --create-init-module to the
    # install command.
    create_init_module = 1

    # On POSIX-systems we run wxgtk-config to determine the C++-compiler
    # flags
    wx_config_script = "wx-config"
    # These lists will be filled automatically below
    wx_cs_params = [[] for i in range(CS_NUM_PARAMS)]

    gdal_config_script = "gdal-config"
    gdal_cs_params = [[] for i in range(CS_NUM_PARAMS)]

elif os.name == "nt":
    #################################################################
    # Windows configuration.
    #
    
    # Directories where Proj4 is installed
    proj4_prefix = r"D:\cygwin\home\user\projects\thuban\proj-4.4.7\src"
    proj4_incdir =  proj4_prefix
    proj4_libdir =  proj4_prefix
    proj4_lib = "proj_i"

    # Define include and lib directories for wxWindows and 
    wx_prefix = r"d:\cygwin\home\user\projects\thuban\wxPython-2.4.0.7"
    wx_inc = [os.path.join(wx_prefix, 'lib', 'mswdllh'),
              os.path.join(wx_prefix, "include")]
    wx_lib = [os.path.join(wx_prefix, "lib")]

    # Define include and lib directories for GDAL 
    gdal_prefix = r"D:\cygwin\home\user\projects\thuban\gdal-1.1.8"
    gdal_inc = [os.path.join(gdal_prefix, 'alg'),
                os.path.join(gdal_prefix, 'ogr'),
                os.path.join(gdal_prefix, 'port'),
                os.path.join(gdal_prefix, 'core')]
    gdal_lib = [gdal_prefix]

    #
    # Unless you use a wxPython version other than 2.4.0, you probably
    # shouldn't have to modify anything below here
    ##################################################################
    
    # Installation prefix. Just install relative to current directory by
    # default. This is only the default value, you can override it on
    # the command line with the install command's --prefix option
    prefix = r"install"

    # Whether to create the thubaninit module. You can override this
    # value on the commandline with the --create-init-module to the
    # install command. By default we don't create it under NT because we
    # most often run install only as part of bdist_inno where we can't
    # really create because it needs information only known at install
    # time.
    create_init_module = 0

    # There doesn't seem to be an easy way to get at the wx compiler
    # flags, so we define them here. These flags work for us with
    # wxPython 2.3.1. They may have to be modified for other versions.

    # there's no config script.
    wx_config_script = ""
    
    wx_cs_params = [[] for i in range(CS_NUM_PARAMS)]

    # the values of wx_defs and wx_libs. copied from the wxPython
    # setup.py
    wx_cs_params[CS_DEFS] = \
              [ ('WIN32', None),        # Some of these are no longer
                ('__WIN32__', None),    # necessary.  Anybody know which?
                ('_WINDOWS', None),
                ('__WINDOWS__', None),
                ('WINVER', '0x0400'),
                ('__WIN95__', None),
                ('STRICT', None),

                ('__WXMSW__', None),
                ('WXUSINGDLL', '1'),

                ('SWIG_GLOBAL', None),
                ('HAVE_CONFIG_H', None),
                ('WXP_USE_THREAD', '1'),
                ]
    
    wx_cs_params[CS_INCDIRS] = wx_inc
    wx_cs_params[CS_LIBDIRS] = wx_lib
    wx_cs_params[CS_LIBS] = ["wxmsw24h"] \
                      + ['kernel32', 'user32', 'gdi32', 'comdlg32',
                         'winspool', 'winmm', 'shell32', 'oldnames',
                         'comctl32', 'ctl3d32', 'odbc32', 'ole32', 'oleaut32',
                         'uuid', 'rpcrt4', 'advapi32', 'wsock32']

    gdal_config_script = ""
    gdal_cs_params = [[] for i in range(CS_NUM_PARAMS)]

    gdal_cs_params[CS_INCDIRS] = gdal_inc
    gdal_cs_params[CS_LIBDIRS] = gdal_lib
    gdal_cs_params[CS_LIBS] = ["gdal_i"]

else:
    raise RuntimeError("Unsupported platform " + os.name)


######################################################################
#
#  There's nothing beyond this point that has to be modified for a
#  normal installation
#
######################################################################


#
#   Functions to determine wxWindows config on POSIX systems
#

def run_script(cmdline):
    """Run command and return its stdout or none in case of errors"""
    pipe = os.popen(cmdline)
    result = pipe.read()
    if pipe.close() is not None:
        print '"' + cmdline + '"', 'failed'
        return None
    return result


def run_cs_script(command, store):
    # first, determine the C++ preprocessor flags Use --cflags here
    # because it seems that older version don't have --cxxflags and
    # newer ones return the same result for both
    flags = run_script(command + ' --cflags ')
    if flags is None:
        return False
    for flag in split(flags):
        start = flag[:2]
        value = flag[2:]
        if start == "-I":
            store[CS_INCDIRS].append(value)
        elif start == "-D":
            store[CS_DEFS].append((value, None))

    # determine the library flags
    flags = run_script(command + ' --libs')
    if flags is None:
        return False
    for flag in split(flags):
        start = flag[:2]
        value = flag[2:]
        if start == "-L":
            store[CS_LIBDIRS].append(value)
        elif start == "-l":
            store[CS_LIBS].append(value)

    return True

if wx_config_script:
    # if there's a wx config script, run it to determine the configuration
    run_cs_script(wx_config_script, wx_cs_params)

if gdal_config_script:
    # if there's a gdal config script, run it to determine the configuration
    include_gdal = include_gdal \
                   and run_cs_script(gdal_config_script, gdal_cs_params)

#
# Define some extension and python modules
#
# The C-extension names are prefixed with "Lib." so they get put into
# the Lib/ subdirectory. Lib/ is not really a package but distutils
# doesn't care

# subdirectory containing the distutil extensions
ext_dir = "libraries"

# subdirectory with some shapelib files
shp_dir = ext_dir + "/shapelib"

# lists to fill with the module descriptions
extensions = []
py_modules = []


#
# Thuban specific modules
#

extensions.append(Extension("Lib.wxproj",
                            [ext_dir + "/thuban/wxproj.cpp"],
                            include_dirs = ([shp_dir, proj4_incdir,
                                             ext_dir + "/pyshapelib/"]
                                            + wx_cs_params[CS_INCDIRS]),
                            define_macros = wx_cs_params[CS_DEFS],
                            library_dirs = [proj4_libdir] + 
                                           wx_cs_params[CS_LIBDIRS],
                            libraries = [proj4_lib] + wx_cs_params[CS_LIBS]))


#
# shapelib wrappers are also distributed with thuban
#

extensions.append(Extension("Lib.shapelibc",
                            [ext_dir + "/pyshapelib/shapelib_wrap.c",
                             shp_dir + "/shpopen.c",
                             shp_dir + "/shptree.c"],
                            include_dirs = [shp_dir]))
extensions.append(Extension("Lib.shptree",
                            [ext_dir + "/pyshapelib/shptreemodule.c"],
                            include_dirs = [shp_dir]))
extensions.append(Extension("Lib.dbflibc",
                            [ext_dir + "/pyshapelib/dbflib_wrap.c",
                             shp_dir + "/dbfopen.c"],
                            include_dirs = [shp_dir]))
for name in ("shapelib", "dbflib"):
    py_modules.append(ext_dir + "/pyshapelib/" + name)

#
# PROJ4 bindings are also distributed with thuban
#
extensions.append(Extension("Lib.Projectionc",
                            [ext_dir + "/pyprojection/Projection_wrap.c"],
                            include_dirs = [proj4_incdir],
                            library_dirs = [proj4_libdir],
                            libraries = [proj4_lib]))
py_modules.append(ext_dir + "/pyprojection/Projection")


#
#  Data files
#

data_files = []

# Resources
for d, patterns in [("Resources/Bitmaps",
                    ("Resources/Bitmaps/*.xpm",)),
                   ("Resources/Projections",
                    ("Resources/Projections/*.proj",)),
                   ("Resources/XML",
                    ("Resources/XML/*.dtd",)),
                   ("Extensions/importAPR/samples",
                    ("Extensions/importAPR/samples/README",
                     "Extensions/importAPR/samples/*.apr")),
                   ]:
    for pattern in patterns:
        data_files.append((d, glob.glob(pattern)))
if os.path.isdir("Resources/Locale"):
    for d in os.listdir("Resources/Locale"):
        data_files.append(("Resources/Locale/" + d +"/LC_MESSAGES",
                           ["Resources/Locale/"+ d +"/LC_MESSAGES/thuban.mo"]))

#
#       Command definitions
#
# So far distutils are only meant to distribute python extensions, not
# complete applications, so we have to redefine a few commands
#


# Much of the data_dist command is directly copied from the distutils'
# sdist command
class data_dist(Command):

    description = "create a data distribution (tarball, zip file, etc.)"

    user_options = [
        ('formats=', None,
         "formats for source distribution (comma-separated list)"),
        ('keep-temp', 'k',
         "keep the distribution tree around after creating " +
         "archive file(s)"),
        ('dist-dir=', 'd',
         "directory to put the source distribution archive(s) in "
         "[default: dist]"),
        ]

    boolean_options = ['keep-temp']

    def initialize_options (self):
        self.formats = None
        self.keep_temp = 0
        self.dist_dir = None

    def finalize_options (self):
        self.ensure_string_list('formats')
        if self.formats is None:
            self.formats = ["zip"]
        bad_format = archive_util.check_archive_formats(self.formats)
        if bad_format:
            raise DistutilsOptionError, \
                  "unknown archive format '%s'" % bad_format

        if self.dist_dir is None:
            self.dist_dir = "dist"


    def run(self):
        # 'filelist' contains the list of files that will make up the
        # manifest
        self.filelist = FileList()
        
        # Do whatever it takes to get the list of files to process.
        # File list is accumulated in 'self.filelist'.
        self.get_file_list()

        # Otherwise, go ahead and create the source distribution tarball,
        # or zipfile, or whatever.
        self.make_distribution()

    def get_file_list(self):
        """Figure out the list of files to include in the data
        distribution, and put it in 'self.filelist'.
        """
        self.filelist.findall("Data")
        self.filelist.include_pattern("*", anchor = 0)
        self.filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1)
        self.filelist.sort()
        self.filelist.remove_duplicates()

    def make_release_tree(self, base_dir, files):
        """Create the directory tree that will become the source
        distribution archive.  All directories implied by the filenames in
        'files' are created under 'base_dir', and then we hard link or copy
        (if hard linking is unavailable) those files into place.
        Essentially, this duplicates the developer's source tree, but in a
        directory named after the distribution, containing only the files
        to be distributed.
        """
        # Create all the directories under 'base_dir' necessary to
        # put 'files' there; the 'mkpath()' is just so we don't die
        # if the manifest happens to be empty.
        self.mkpath(base_dir)
        dir_util.create_tree(base_dir, files,
                             verbose=self.verbose, dry_run=self.dry_run)

        # And walk over the list of files, either making a hard link (if
        # os.link exists) to each one that doesn't already exist in its
        # corresponding location under 'base_dir', or copying each file
        # that's out-of-date in 'base_dir'.  (Usually, all files will be
        # out-of-date, because by default we blow away 'base_dir' when
        # we're done making the distribution archives.)
    
        if hasattr(os, 'link'):        # can make hard links on this system
            link = 'hard'
            msg = "making hard links in %s..." % base_dir
        else:                           # nope, have to copy
            link = None
            msg = "copying files to %s..." % base_dir

        if not files:
            self.warn("no files to distribute -- empty manifest?")
        else:
            self.announce(msg)
        for file in files:
            if not os.path.isfile(file):
                self.warn("'%s' not a regular file -- skipping" % file)
            else:
                dest = os.path.join(base_dir, file)
                self.copy_file(file, dest, link=link)


    def make_distribution (self):
        """Create the source distribution(s).  First, we create the release
        tree with 'make_release_tree()'; then, we create all required
        archive files (according to 'self.formats') from the release tree.
        Finally, we clean up by blowing away the release tree (unless
        'self.keep_temp' is true).  The list of archive files created is
        stored so it can be retrieved later by 'get_archive_files()'.
        """
        # Don't warn about missing meta-data here -- should be (and is!)
        # done elsewhere.
        base_dir = "Thuban-data-" + self.distribution.get_version()
        base_name = os.path.join(self.dist_dir, base_dir)

        self.make_release_tree(base_dir, self.filelist.files)
        archive_files = []              # remember names of files we create
        for fmt in self.formats:
            file = self.make_archive(base_name, fmt, base_dir=base_dir)
            archive_files.append(file)

        self.archive_files = archive_files

        if not self.keep_temp:
            dir_util.remove_tree(base_dir, self.verbose, self.dry_run)



class InstallLocal(Command):

    """
    A new install command to just link (or copy, on non-POSIX systems)
    the extension modules to the top directory so that Thuban can be run
    directly from the source dir.
    """

    description =\
        "Create some symlinks so you can run thuban from the source directory"

    user_options = [
        ('skip-build', None, "skip the build steps"),
        ('create-init-module', None,
         "Create the thubaninit.py module to ease use of Thuban as a library"),
        ('dont-create-init-module', None,
         "Do not create the thubaninit.py module"),
        ]

    boolean_options = ["create-init-module"]
    negative_opt = {'dont-create-init-module' : 'create-init-module'}


    def initialize_options (self):
        self.extensions = None
        self.build_dir = None
        self.skip_build = None
        self.create_init_module = None

    def finalize_options (self):
        self.set_undefined_options("install",
                                   ("build_lib", "build_dir"),
                                   ('skip_build', 'skip_build'))
        self.extensions = self.distribution.ext_modules
        if self.create_init_module is None:
            # by default we create the init module
            self.create_init_module = 1

    def run(self):
        # Make sure we have built everything we need first
        self.build()

        # now do the work. Simply link or copy the Lib dir
        libdir = os.path.join(self.build_dir, "Lib")
        if os.name == "posix":
            # on posix, just link the Lib dir
            self.link_dir(libdir, "Lib")
        else:
            self.copy_tree(libdir, "Lib")

        # create the init module if desired
        if self.create_init_module:
            # create the init module
            initfilename = "thubaninit.py"
            contents = thubaninit_contents("")
            self.execute(write_file, (initfilename, contents),
                         "Create %s" % initfilename)

    def link_dir(self, src, dest):
        """Create a symbolic link dest pointing to src"""
        if self.verbose:
            self.announce("symlinking %s -> %s" % (src, dest))
        if self.dry_run:
            return

        if not (os.path.exists(dest) and os.path.samefile(src, dest)):
            os.symlink(src, dest)

    def build (self):
        if not self.skip_build:
            if self.distribution.has_pure_modules():
                self.run_command('build_py')
            if self.distribution.has_ext_modules():
                self.run_command('build_ext')



class thuban_build_py(build_py):

    """
    A new build_py that can deal with both packages and modules in one
    distribution.
    """

    # FIXME: When Thuban can rely on Python 2.3 as the oldest supported
    # Python release we don't need to override the run and
    # find_all_modules methods anymore. distutils will allow both python
    # modules and packages starting with 2.3.

    def run(self):
        """The same the as the original in build_py revision 1.33 except
        that this allows both packages and modules to be in one
        distribution
        """
        if not self.py_modules and not self.packages:
            return

        # Now we're down to two cases: 'py_modules' only and 'packages' only.
        if self.py_modules:
            self.build_modules()
        if self.packages:
            self.build_packages()

        self.byte_compile(self.get_outputs(include_bytecode=0))

    def find_modules (self):
        """Thuban specific version of build_py.find_modules. Unlike the
        original version, we assume that the modules in self.py_modules
        can contain directories and are all to be placed into the same
        subdirectory, Lib, in the build directory. This is achieved by
        returning the modules as a list (package, module, filename)
        where package is 'Lib', module is the basename of the module name
        and filename is the filename relative to the package root.
        """
        modules = []
        for module in self.py_modules:
            module_base = os.path.basename(module)
            module_file = module + ".py"
            if not self.check_module(module, module_file):
                continue

            modules.append(("Lib", module_base, module_file))
        return modules

    def find_all_modules (self):
        # same as find_all_modules of the original build_py command
        # (rev. 1.33) but handle installations with both modules and
        # packages. Needed here so tha the get_outputs works correctly
        modules = []
        if self.py_modules:
            modules.extend(self.find_modules())
        if self.packages:
            for package in self.packages:
                package_dir = self.get_package_dir(package)
                m = self.find_package_modules(package, package_dir)
                modules.extend(m)

        return modules


thubaninit_contents_start = """
# This module was automatically generated by Thuban's install script
'''Import this module once per program (best place is probably the file
that ends up as your __main__ module) to be able to import Thuban
afterwards.

Usage:

import thubaninit
import Thuban
'''
import sys, os
"""

thubaninit_contents_thubaninitdir = """
sys.path.insert(0, %(thubandir)s)
"""
thubaninit_contents_otherdirs = """
# Put the Lib dir into the path. The Lib dir contains some extra Python
# modules
import Thuban
thubandir = os.path.join(Thuban.__path__[0], '..')
dir = os.path.join(thubandir, "Lib")
if os.path.isdir(dir):
    sys.path.insert(0, dir)
"""

def thubaninit_contents(thubandir):
    """Return the contents of the the thubaninit file as a list of lines.

    The parameter thubandir is the parent directory where the Thuban/
    package or the empty string if the thubaninit file itself will be
    located in that direcory as well.
    """
    contents = thubaninit_contents_start
    if thubandir:
        thubandir = repr(thubandir)
        contents += thubaninit_contents_thubaninitdir % locals()
    contents += thubaninit_contents_otherdirs
    return contents.split("\n")


class ThubanInstall(install):

    """
    Thuban specific install command.

    Extend the standard install command to symlink the installed script
    to $prefix/bin/
    """

    user_options = install.user_options[:]
    user_options.extend([("do-symlink", None,
                          "Create a symlink to the script in <prefix>/bin."
                        "(default on posix systems and only relevant there)"),

                         ("extra-files", None,
                          "List of filenames or (src, dest) pairs describing"
                          " extra files to install "
                          "(can only be set from within setup.py"),

                         ("create-init-module=", None,
                          "If true, create a module in the site-packages"
                          " directory that tweaks sys.path to let you easily"
                          " import thuban modules from outside of thuban."),
                         ("init-module-dir=", None,
                          "Directory in which to create the init module."
                          " Defaults to Python's site-packages directory."),
                         ])

    boolean_options = install.boolean_options[:]
    boolean_options.append("do-symlink")
    boolean_options.append("create-init-module")

    def initialize_options(self):
        self.do_symlink = None
        self.extra_files = []

        # initialize the create_init_module flag from the global
        # determined at runtime
        self.create_init_module = create_init_module
        self.init_module_dir = None
        install.initialize_options(self)

    def finalize_options(self):
        if self.do_symlink is None:
            if os.name == "posix":
                self.do_symlink = 1
            else:
                self.do_symlink = 0
        install.finalize_options(self)
        self.expand_with_pure_python_dirs(["init_module_dir"])

    def expand_with_pure_python_dirs(self, attrs):
        """Expand the attributes with default values of base and platbase"""
        # it seems that the values for "prefix" and "exec_prefix" in
        # self.config_vars are the corresponding values used by the
        # python interpreter, so we just assign these to "base" and
        # "platbase".
        config_vars = self.config_vars.copy()
        config_vars["base"] = self.config_vars["prefix"]
        config_vars["platbase"] = self.config_vars["exec_prefix"]
        for attr in attrs:
            val = getattr(self, attr)
            if val is not None:
                if os.name == 'posix':
                    val = os.path.expanduser(val)
                val = subst_vars(val, config_vars)
                setattr(self, attr, val)

    def select_scheme(self, scheme):
        """Extend the inherited method to set init_module_dir"""
        install.select_scheme(self, scheme)
        # only set init_module_dir if it wasn't set by the user
        if self.init_module_dir is None:
            self.init_module_dir = INSTALL_SCHEMES[scheme]['purelib']

    def convert_paths(self, *args):
        """Extend the inherited method so that we can remember some filename
        """
        # remember the installation directory before its root gets
        # changed
        self.install_lib_orig = self.install_lib
        apply(install.convert_paths, (self,) + args)

    def run(self):
        install.run(self)
        for item in self.extra_files:
            if type(item) == TupleType:
                src, dest = item
            else:
                src = dest = item
            self.copy_file(convert_path(src),
                           os.path.join(self.root, convert_path(dest)))

        if os.name == "posix" and self.do_symlink:
            install_scripts = self.install_scripts
            if self.root:
                install_scripts = install_scripts[len(self.root):]
            scriptfile = os.path.join(install_scripts, "thuban.py")
            bindir = os.path.join(self.prefix, "bin")
            if self.root:
                bindir = change_root(self.root, bindir)
            binfile = os.path.join(bindir, "thuban")
            self.mkpath(bindir)
            self.link_file(scriptfile, binfile)

        if self.create_init_module:
            # create the init module
            initfilename = self.thuban_init_filename()
            if self.root:
                initfilename = change_root(self.root, initfilename)
            contents = thubaninit_contents(self.install_lib_orig)
            self.mkpath(os.path.dirname(initfilename))
            self.execute(write_file, (initfilename, contents),
                         "Create %s" % initfilename)

    def link_file(self, src, dest):
        """Create a symbolic link dest pointing to src.

        Unlike the symlink variant of the command object's copy_file
        method, this method even performs the link if src doesn't exist.
        This is useful when installing with an alternat root directory"""
        if self.verbose:
            self.announce("symlinking %s -> %s" % (src, dest))
        if self.dry_run:
            return

        if not os.path.exists(dest):
            os.symlink(src, dest)

    def thuban_init_filename(self):
        """Return the filename for the init-module"""
        # Since we override the normal install dirs to point to our own
        # prefix we have to reach into installed
        return self.init_module_dir + "/thubaninit.py"

    def get_outputs (self):
        outputs = install.get_outputs(self)
        for item in self.extra_files:
            if type(item) == TupleType:
                src, dest = item
            else:
                src = dest = item
            outputs.append(os.path.join(self.root, convert_path(dest)))
        if os.name == "posix" and self.do_symlink:
            bindir = os.path.join(self.prefix, "bin")
            if self.root:
                bindir = change_root(self.root, bindir)
            binfile = os.path.join(bindir, "thuban")
            outputs.append(binfile)
        if self.create_init_module:
            initfilename = self.thuban_init_filename()
            if self.root:
                initfilename = change_root(self.root, initfilename)
            outputs.append(initfilename)
        return outputs


# scripts to override some of the commands put into the spec-file by the
# bdist_rpm command.

bdist_rpm_prep_script = '''
%setup
cp libraries/pyshapelib/{README,README.pyshapelib}
cp libraries/pyshapelib/{COPYING,COPYING.pyshapelib}
cp libraries/pyprojection/{LICENSE,LICENSE.pyprojection}
'''

bdist_rpm_build_script = '''
env PATH="$PATH:%(prefix)s/lib/wxPython/bin:/usr/lib/wxPython/bin" CFLAGS="$RPM_OPT_FLAGS" %(python)s setup.py build
'''

bdist_rpm_install_script = '''
%(python)s setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES \
   --prefix=%(prefix)s
'''


class thuban_bdist_rpm(bdist_rpm):

    """Thuban specific RPM distribution command"""

    user_options = bdist_rpm.user_options[:]
    user_options.extend([("prefix=", None, "Install prefix for the RPM"),
                         ])

    def initialize_options(self):
        # per default, RPMs are installed in /usr
        self.prefix = "/usr/"

        # create the scripts we want to override. We actually fill them
        # with contents later because some values we put into those
        # scripts such as the python interpreter to use are only known
        # then.
        open("bdist_rpm_prep", "w").close()
        open("bdist_rpm_build", "w").close()
        open("bdist_rpm_install", "w").close()
        bdist_rpm.initialize_options(self)

    def _make_spec_file(self):
        # create the scripts for the spec-file. Now we know the python
        # interpreter to use.
        open("bdist_rpm_prep", "w").write(bdist_rpm_prep_script)

        build = bdist_rpm_build_script % {"python": self.python,
                                          "prefix": self.prefix}
        open("bdist_rpm_build", "w").write(build)

        install = bdist_rpm_install_script % {"python": self.python,
                                              "prefix": self.prefix}
        open("bdist_rpm_install", "w").write(install)

        #
        return bdist_rpm._make_spec_file(self)


class bdist_inno(Command):

    """Command to create a windows installer with Inno Setup"""

    description = "Create a windows installer with Inno Setup"

    user_options = [
        ('skip-build', None, "skip the build steps"),
        ('bdist-dir=', None,
         "temporary directory for creating the distribution"),
        ('run-inno', None,
         "Run inno-setup to create the installer. On by default on nt"),
        ('iss-name', None,
         "The name of the iss file to generate. "
         "Shouldn't contain directories"),

        # Parameters for the Inno Setup script
        ('copyright', None, "Copyright notice for the Inno Setup file"),
        ('default-dir-name', None,
         "Default installation directory. Defaults to '{pf}\\<name>'"),
        ('default-group-name', None,
         "Default program group name. Defaults to <name>'"),
        ("license-file", None, "File containing the license."),
        ("output-basename", None,
         "Base filename for the Inno Setup output "
         "(defaults to <name>-<version>-<issrevision>)."),
        ("iss-revision", None,
         "revision of the generated installer of the package version"),

        ("icons-entries", None,
         "List if InnoIconItems "
         "(this can only be set from inside the setup.py script)"),
        ]

    boolean_options = ["do-symlink"]

    def initialize_options(self):
        self.skip_build = 0
        self.bdist_dir = None
        self.run_inno = None
        self.iss_name = None
        self.copyright = ""
        self.default_dir_name = None
        self.default_group_name = None
        self.license_file = None
        self.output_basename = None
        self.iss_revision = None
        self.icons_entries = []

    def finalize_options(self):
        self.set_undefined_options("install",
                                   ('skip_build', 'skip_build'))
        if self.bdist_dir is None:
            bdist_base = self.get_finalized_command('bdist').bdist_base
            self.bdist_dir = os.path.join(bdist_base, 'inno')

        if self.run_inno is None:
            self.run_inno = os.name == "nt"

        name = self.distribution.get_name()
        if self.iss_name is None:
            self.iss_name = name + '.iss'

        if self.default_dir_name is None:
            self.default_dir_name = "{pf}\\" + name
        if self.default_group_name is None:
            self.default_group_name = name

        if self.iss_revision is None:
            self.iss_revision = 0
        if self.output_basename is None:
            self.output_basename = "%s-%s-%d" \
                                   % (name, self.distribution.get_version(),
                                      self.iss_revision)

    def run(self, install_options = None):
        """Execute the command. install_options if given, should be a
        directory of additional options to set in the install step"""
        # Obviously have to build before we can install

        if not self.skip_build:
            self.run_command('build')

        # Install in a temporary directory
        install = self.reinitialize_command('install')
        install.root = self.bdist_dir
        if install_options is not None:
            for key, value in install_options.items():
                setattr(install, key, value)
        if os.name != 'nt':
            # Must force install to use the 'nt' scheme;
            install.select_scheme('nt')

        self.announce("installing to %s" % self.bdist_dir)
        install.ensure_finalized()
        install.run()

        # Create the iss file
        iss_file = os.path.join(self.bdist_dir, self.iss_name)
        self.execute(write_file, (iss_file, self.generate_iss()),
                     "Create Inno Setup script file %s" % iss_file)

        # and invoke
        if self.run_inno:
            self.spawn(["iscc", iss_file])

    def generate_iss(self):
        """Return the contents of the iss file as list of strings, one
        string per line"""

        # first, turn the icons entries into a more usable form
        icons = {}
        for item in self.icons_entries:
            icons[item.filename] = item

        iss = []

        name = self.distribution.get_name()
        iss.extend(["[Setup]",
                    "AppName=" + name,
                    "AppVerName=" + name + " "+self.distribution.get_version(),
                    "DefaultDirName=" + self.default_dir_name,
                    "DefaultGroupName=" + self.default_group_name,
                    ])
        if self.copyright:
            iss.append("AppCopyright=" + self.copyright)
        if self.license_file:
            iss.append("LicenseFile=" + self.license_file)

        iss.append("OutputBasefilename=" + self.output_basename)

        iss.append("")
        iss.append("[Files]")

        install = self.get_finalized_command("install")
        install_scripts = self.get_finalized_command("install_scripts")
        script_files = install_scripts.get_outputs()
        prefixlen = len(self.bdist_dir) + len(os.sep)
        for filename in install.get_outputs():
            filename = filename[prefixlen:]
            icon = icons.get(filename)
            dirname = os.path.dirname(filename)
            if os.name != "nt":
                # change the separators to \ on non-windos systems
                filename = string.join(string.split(filename, os.sep), "\\")
                dirname =  string.join(string.split(dirname, os.sep), "\\")
            line = 'Source: "%s"' % filename
            if icon is not None:
                # install it as defined in the icon object
                backslash = string.rfind(icon.install_name, "\\")
                if backslash >= 0:
                    dirname = icon.install_name[:backslash]
                    basename = icon.install_name[backslash + 1:]
                else:
                    dirname = ""
                    basename = icon.install_name
                line = '%s; DestDir: "%s"; DestName: "%s"' % (line, dirname,
                                                              basename)
            else:
                line = line + '; DestDir: "{app}\\%s"' % (dirname)
            iss.append(line)

        iss.append("")
        iss.append("[Icons]")
        for icon in self.icons_entries:
            line = 'Name: "{group}\\%s"; Filename: "%s";' \
                   % (icon.title, icon.install_name)
            iss.append(line)

        return iss


class InnoIconItem:

    """Describe one item for the start menu for the Inno Setup installer"""

    def __init__(self, filename, title, install_name = None):
        self.filename = filename
        self.title = title
        if install_name is not None:
            self.install_name = install_name
        else:
            self.install_name = filename


class thuban_bdist_inno(bdist_inno):

    """Thuban specific Inno Setup stuff"""

    def run(self):
        install_options = {
            "prefix": ".",
            "install_lib": "$base",
            "install_data": "$base",
            "install_scripts": "$base",
            "warn_dir": 0,
            "extra_files": ["COPYING", "Lib/proj.dll"],
            }
        # don't make a symlink because we're simulating windows, so
        # that we can generate the iss-file even on Linux
        install_options["do_symlink"] = 0

        bdist_inno.run(self, install_options)

class thuban_build_docs(Command):

    """Command to generate documentation from source code."""

    description = "Generate documentation."

    user_options = []

    def initialize_options(self): pass

    def finalize_options(self): pass

    def run(self, install_options = None):
        self.spawn(["happydoc", "-d./Doc", "./Thuban"])

class thuban_build_ext(build_ext):

    """Extend the build_ext command to optionally include the
    GDAL extension.
    """

    user_options = build_ext.user_options[:]
    user_options.extend([("with-gdal", None, "Include GDAL support."),
                         ("without-gdal", None, "Don't include GDAL support.")])

    boolean_options = ["with-gdal"]
    negative_opt = {'without-gdal' : 'with-gdal'}

    def initialize_options(self):
        self.with_gdal = True
        build_ext.initialize_options(self)

    def finalize_options(self): 
        build_ext.finalize_options(self)
        if self.with_gdal and include_gdal:
            self.extensions.append(Extension("Lib.gdalwarp",
                                [ext_dir + "/thuban/gdalwarp.cpp",
                                ext_dir + "/thuban/cpl_mfile.cpp",
                                ext_dir + "/thuban/bmpdataset.cpp"],
                                include_dirs = gdal_cs_params[CS_INCDIRS] +
                                               [ext_dir + "/thuban/"],
                                define_macros = gdal_cs_params[CS_DEFS],
                                library_dirs = gdal_cs_params[CS_LIBDIRS],
                                libraries = gdal_cs_params[CS_LIBS]))

    def run(self, install_options = None):
        build_ext.run(self)

#
#   Run the script
#

long_description = """\
Thuban is a viewer for geographic data written in Python
"""

setup(name = "Thuban",
      version = "1.0rc1",
      description = "Geographic data viewer",
      long_description = long_description,
      licence = "GPL",
      author = "Intevation GmbH",
      author_email = "thuban@intevation.de",
      url = "http://thuban.intevation.de/",

      scripts = ["thuban.py"],
      packages = ["Thuban", "Thuban.Lib", "Thuban.Model", "Thuban.UI",
                  "Extensions", "Extensions.gns2shp", "Extensions.wms",
                  "Extensions.importAPR", "Extensions.profiling"],
      ext_modules = extensions,
      py_modules = py_modules,
      data_files = data_files,

      # defaults for the install command
      options = {"install":
                 # prefix defaults to python's prefix normally
                 {"prefix": prefix,
                  # make sure both libs and scripts are installed in the
                  # same directory.
                  "install_lib": "$base/lib/thuban",
                  "install_scripts": "$base/lib/thuban",
                  "install_data": "$base/lib/thuban",

                  # Don't print warning messages about the lib dir not
                  # being on Python's path. The libraries are Thuban
                  # specific and are installed just for Thuban. They'll
                  # be automatically on Python's path when Thuban is run
                  "warn_dir": 0,
                  },
                 "bdist_inno":
                 {"icons_entries": [InnoIconItem(".\\thuban.py",
                                                 "Start Thuban",
                                                 "{app}\\thuban.pyw")],
                  "license_file": "COPYING",
                  }
                 },
      cmdclass = {"build_py": thuban_build_py,
                  "install_local": InstallLocal,
                  "install": ThubanInstall,
                  "bdist_rpm": thuban_bdist_rpm,
                  "bdist_inno": thuban_bdist_inno,
                  "data_dist": data_dist,
                  "build_docs": thuban_build_docs,
                  "build_ext": thuban_build_ext
                  })


