#!/usr/bin/python
###########################################################################
# tdedistutils - description                                              #
# ------------------------------                                          #
# begin     : Thu Apr 21 2005                                             #
# copyright : (C) 2005 by Simon Edwards                                   #
# email     : simon@simonzone.com                                         #
#                                                                         #
###########################################################################
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU Library General Public License as       #
#   published by the Free Software Foundation; either version 2 of the    #
#   License, or (at your option) any later version.                       #
#                                                                         #
###########################################################################

import distutils.core
from distutils.core import Extension
from distutils.cmd import Command
from distutils.dist import Distribution
from distutils.command.build import build
from distutils.command.install import install
from distutils.command.install_scripts import install_scripts
from distutils.command.install_data import install_data
from distutils.command.install_lib import install_lib
from distutils.util import change_root, convert_path
from distutils.spawn import find_executable,spawn
from distutils import log
from distutils import file_util
from distutils import dir_util
from distutils.util import byte_compile
import tqtuicompiler
import stat
import os,os.path,glob
import sys
import sysconfig
from types import *

INSTALL_LIST = 'install_log.txt'


###########################################################################
def setup(**arg_dict):

    if 'cmdclass' not in arg_dict:
        arg_dict['cmdclass'] = {}

    cmdclass = arg_dict['cmdclass']

    arg_dict.setdefault('data_files',[])

    kdecmdclass = {'install' : InstallKDE,
        'install_executable_links' : InstallExecutableLinks,
        'install_application_data' : InstallApplicationDataAndLinks,
        'build_messages' : BuildI18NMessages,
        'install_messages' : InstallI18NMessages,
        'update_messages' : UpdateI18NMessages,
        'checkpytqt' : CheckPyTQt,
        'checkpytde' : CheckPyTDE,
        'uninstall' : Uninstall,
        'build' : BuildKDE,
        'build_kcm' : BuildKControlModule,
        'install_kcm' : InstallKControlModule,
        'build_html' : BuildDocbookHTML,
        'install_html' : InstallDocbookHTML,
        'install_lib' : InstallLibWithRoot,
        'build_tdeioslave' : BuildTdeioslave,
        'install_tdeioslave' : InstallTdeioslave}

    for key in kdecmdclass.keys():
        cmdclass.setdefault(key,kdecmdclass[key])

    arg_dict.setdefault('distclass',KDEDistribution)

    distutils.core.setup(*[], **arg_dict)

###########################################################################
class KDEDistribution(Distribution):
    def __init__(self,attrs=None):
        self.min_kde_version = None
        self.min_tqt_version = None
        self.kcontrol_modules = None
        self.tdeioslaves = None
        self.executable_links = None
        self.docbooks = None
        self.application_data = None
        self.i18n = None
        Distribution.__init__(self,attrs)

###########################################################################
def has_kcms(self):
    if self.distribution.kcontrol_modules is None:
        return 0
    return  len(self.distribution.kcontrol_modules)!=0

def has_docbook_html(self):
    if self.distribution.docbooks is None:
        return 0
    return  len(self.distribution.docbooks)!=0

def has_messages(self):
    if self.distribution.i18n is None:
        return 0
    return  len(self.distribution.i18n)!=0

def has_application_data(self):
    if self.distribution.application_data is None:
        return 0
    return  len(self.distribution.application_data)!=0

def has_tdeioslaves(self):
    if self.distribution.tdeioslaves is None:
        return 0
    return  len(self.distribution.tdeioslaves)!=0

###########################################################################
# Our slightly extended build command. This also does kcontrol modules.
class BuildKDE(build):

    user_options = build.user_options[:]
    user_options.append( ('msgfmt-exe=',None,'Path to the msgfmt executable') )
    user_options.append( ('meinproc-exe=',None,'Path to the meinproc executable') )

    sub_commands = build.sub_commands[:]
    sub_commands.append( ('build_kcm',has_kcms) )
    sub_commands.append( ('build_tdeioslave',has_tdeioslaves) )
    sub_commands.append( ('build_html',has_docbook_html) )
    sub_commands.append( ('build_messages',has_messages) )

    def __init__(self,dist):
        build.__init__(self,dist)
        self.has_kcms = has_kcms
        self.has_tdeioslaves = has_tdeioslaves
        self.has_docbook_html = has_docbook_html
        self.has_messages = has_messages

    def initialize_options(self):
        self.msgfmt_exe = None
        self.meinproc_exe = None
        build.initialize_options(self)

    def finalize_options(self):
        build.finalize_options(self)

        if self.msgfmt_exe is None:
            # Find msgfmt
            canidatepaths = []
            canidatepaths.append( ask_kde_config('--install exe --expandvars').strip() )
            self.msgfmt_exe = FindExeOnPath('msgfmt',canidatepaths)
            if self.msgfmt_exe is None:
                raise SystemExit("Unable to find 'msgfmt', needed to build i18n messages.")

        if self.meinproc_exe is None:
            # Find meinproc
            canidatepaths = []
            canidatepaths.append( ask_kde_config('--install exe --expandvars').strip() )
            self.meinproc_exe = FindExeOnPath('meinproc',canidatepaths)
            if self.meinproc_exe is None:
                raise SystemExit("Unable to find 'meinproc', needed to generate Docbook HTML documentation.")

###########################################################################
def has_executable_links(self):
    if self.distribution.executable_links is None:
        return 0
    return  len(self.distribution.executable_links)!=0

###########################################################################
class InstallKDE(install):
    user_options = install.user_options[:]
    user_options.append( ('kde-prefix=',None,"TDE installation prefix") )
    user_options.append( ('install-messages=',None,"installation directory for i18n message files") )
    user_options.append( ('install-html=',None,"installation directory for Docbook HTML files") )
    user_options.append( ('install-cmd=',None,"Command to use to install the files") )
    user_options.append( ('install-xdg-apps=',None,"directory for XDG app files") )
    user_options.append( ('install-kcm=',None,"directory for kcm library files") )
    user_options.append( ('install-tdeioslave',None,"directory for tdeioslave library files") )
    user_options.append( ('install-protocol',None,"directory for tdeioslave protocol files") )

    sub_commands = install.sub_commands[:]
    sub_commands.insert(0, ('checkpytde',None) )
    sub_commands.insert(0, ('checkpytqt',None) )
    sub_commands.append( ('install_executable_links',has_executable_links) )
    sub_commands.append( ('install_messages',has_messages) )
    sub_commands.append( ('install_html',has_docbook_html) )
    sub_commands.append( ('install_kcm',has_kcms) )
    sub_commands.append( ('install_tdeioslave',has_tdeioslaves) )
    sub_commands.append( ('install_application_data',has_application_data) )

    def initialize_options(self):
        self.kde_prefix = None
        self.install_messages = None
        self.install_html = None
        self.install_cmd = None
        self.install_xdg_apps = None
        self.install_kcm = None
        self.install_tdeioslave = None
        self.install_protocol = None
        self.install_application_data = None
        install.initialize_options(self)

    def finalize_options(self):
        # If no install prefix was provided, then we try to detect the KDE install prefix.
        self.user_supplied_kde_prefix = True

        if self.install_scripts is None:
            if self.kde_prefix is not None:
                self.install_scripts = os.path.join(self.kde_prefix,'bin')
            else:
                self.announce("Detecting TDE 'bin' directory...")
                self.install_scripts = ask_kde_config('--install exe --expandvars').strip()
                self.announce("   ...TDE 'bin' directory is %s" % self.install_scripts)

        if self.install_application_data is None:
            if self.kde_prefix is not None:
                self.install_application_data = os.path.join(self.kde_prefix,'share/apps',self.distribution.metadata.name)
            else:
                self.announce("Detecting TDE application directory...")
                kdeappdir = ask_kde_config('--install data --expandvars').strip()
                self.announce("  ...TDE application directory is %s" % self.install_application_data)
                self.install_application_data = os.path.join(kdeappdir,self.distribution.metadata.name)

        if self.install_messages is None:
            if self.kde_prefix is not None:
                self.install_messages = os.path.join(self.kde_prefix,'share/locale')
            else:
                self.announce("Detecting TDE messages directory...")
                self.install_messages = ask_kde_config('--install locale --expandvars').strip()
                self.announce("  ...TDE messages directory is %s" % self.install_messages)

        if self.install_html is None:
            if self.kde_prefix is not None:
                self.install_html = os.path.join(self.kde_prefix,'share/doc/tde/HTML')
            else:
                self.announce("Detecting TDE HTML directory...")
                self.install_html = ask_kde_config('--install html --expandvars').strip()
                self.announce("  ...TDE HTML directory is %s" % self.install_html)

        if self.kde_prefix is None:
            self.announce("Detecting TDE install prefix...")
            self.kde_prefix = ask_kde_config('--prefix').strip()
            self.announce("  ...TDE install prefix is %s" % self.kde_prefix)
            self.user_supplied_kde_prefix = False

        if self.install_cmd is None:
            self.announce("Detecting 'install' command...")
            # Ok, time to find the install command.
            self.install_cmd = find_executable('install')
            if self.install_cmd is None:
                raise SystemExit("Unable to find the 'install' command, needed to install libraries.")
            self.announce("  ...'install' command is %s" % self.install_cmd)

        if self.install_xdg_apps is None:
            self.announce("Detecting XDG apps directory...")
            self.install_xdg_apps = ask_kde_config('--install xdgdata-apps --expandvars').strip()
            self.announce("  ...XDG apps directory is %s" % self.install_xdg_apps)

        if self.install_kcm is None:
            self.announce("Detecting kcm library directory...")
            self.install_kcm = ask_kde_config('--install module --expandvars').strip()
            self.announce("  ...kcm library directory is %s" % self.install_kcm)

        if self.install_tdeioslave is None:
            self.announce("Detecting tdeioslave library directory...")
            self.install_tdeioslave = ask_kde_config('--install module --expandvars').strip()
            self.announce("  ...tdeioslave library directory is %s" % self.install_tdeioslave)

        if self.install_protocol is None:
            self.announce("Detecting tdeioslave protocol directory...")
            self.install_protocol = ask_kde_config('--install services --expandvars').strip()
            self.announce("  ...tdeioslave protocol directory is %s" % self.install_protocol)

        install.finalize_options(self)

        if self.root is not None:
            self.change_roots('application_data','html','messages','xdg_apps','kcm')

    def get_command_name(self):
        return 'install'

    def run(self):
        global INSTALL_LIST
        install.run(self)

        # Write out the uninstall list.
        fhandle = open(INSTALL_LIST,'w')
        for item in self.get_outputs():
            fhandle.write(item)
            fhandle.write('\n')
        fhandle.close()

###########################################################################
class InstallApplicationDataAndLinks(install_data):
    def get_command_name(self):
        return 'install_application_data'

    def initialize_options(self):
        install_data.initialize_options(self)

        self.data_files = self.distribution.application_data

    def finalize_options(self):
        self.set_undefined_options('install',
                                   ('install_application_data', 'install_dir'),
                                   ('root', 'root'),
                                   ('force', 'force'),
                                  )

    def run(self):
        self.outfiles.extend(self.mkpath(self.install_dir))
        for f in self.data_files:
            if type(f) is type('String'):
                # it's a simple file, so copy it
                f = convert_path(f)
                if self.warn_dir:
                    self.warn("setup script did not provide a directory for "
                              "'%s' -- installing right in '%s'" %
                              (f, self.install_dir))
                if os.path.isfile(f):
                    (out, _) = self.copy_file(f, self.install_dir)
                    self.outfiles.append(out)
                elif os.path.isdir(f):
                    out = self.copy_tree(f,os.path.join(self.install_dir,f))
                    self.outfiles.extend(out)
                else:
                    self.warn("Setup script can't find file or directory %s needed for installation." % f)
            else:
                # it's a tuple with path to install to and a list of files
                dir = convert_path(f[0])
                if not os.path.isabs(dir):
                    dir = change_root(self.install_dir, dir)
                elif self.root:
                    dir = change_root(self.root, dir)
                self.outfiles.extend(self.mkpath(dir))

                if f[1] == []:
                    # If there are no files listed, the user must be
                    # trying to create an empty directory, so add the
                    # directory to the list of output files.
                    self.outfiles.append(dir)
                else:
                    # Copy files, adding them to the list of output files.
                    for data in f[1]:
                        data = convert_path(data)
                        if os.path.islink(data):
                            # Transplant the link to the new location without changing
                            # where it points to. (ie it is _not_ relocated).
                            dest = os.path.join(dir, os.path.basename(data))
                            if os.path.exists(dest):
                                os.remove(dest)
                            os.symlink(os.readlink(data),dest)
                            log.info("moving link %s -> %s" % (data,dest) )
                            #os.chmod(dest, os.stat(data)[stat.ST_MODE])
                        elif os.path.isdir(data):
                            out = self.copy_tree(data,dir)
                            self.outfiles.extend(out)
                        else:
                            (out, _) = self.copy_file(data, dir)
                            self.outfiles.append(out)

        # Compile the .ui files
        install_cmd = self.get_finalized_command('install')
        prefix = self.install_dir
        if prefix[-1] != os.sep:
            prefix = prefix + os.sep
        self.outfiles.extend(compile_tqtdesigner(self.outfiles, force=1, prefix=prefix, base_dir=install_cmd.prefix, dry_run=self.dry_run))

        if not sys.dont_write_bytecode:
            # Byte compile the .py files (unless it is disabled)
            from distutils.util import byte_compile
            byte_compile(self.outfiles, optimize=0, force=1, prefix=prefix, base_dir=install_cmd.prefix, dry_run=self.dry_run)

        # Add the .pyc files to the list of outfiles.
        self.outfiles.extend( [item+'c' for item in self.outfiles if item.endswith('.py')] )

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name

###########################################################################
class InstallExecutableLinks(Command):
    description = "Install symlinks"

    user_options = []

    def initialize_options(self):
        self.outfiles = []

    def finalize_options(self):
        pass

    def get_command_name(self):
        return 'install_executable_links'

    def run(self):
        # FIXME add cmd options?
        install_script_cmd = self.get_finalized_command('install_scripts')
        install_data_cmd = self.get_finalized_command('install_application_data')

        destination_dir = install_data_cmd.install_dir

        if not os.path.exists(install_script_cmd.install_dir):
            self.outfiles.extend(self.mkpath(install_script_cmd.install_dir))

        if self.distribution.executable_links is not None:
            for link in self.distribution.executable_links:
                symname = os.path.join(install_script_cmd.install_dir,link[0])
                target = os.path.join(destination_dir,link[1])
                log.info("symlinking %s -> %s", symname, target)
                if not self.dry_run:
                    if os.path.islink(symname):
                        os.remove(symname)
                    os.symlink(target,symname)
                self.outfiles.append(symname)

    def get_outputs(self):
        return self.outfiles or []

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name

###########################################################################
# Fix the --root option for the install_lib command.
class InstallLibWithRoot(install_lib):
    user_options = install_lib.user_options[:]
    user_options.append( ('root=',None,"install everything relative to this alternate root directory") )

    def initialize_options(self):
        install_lib.initialize_options(self)
        self.root = None

    def finalize_options(self):
        own_install_dir = self.install_dir is not None

        install_lib.finalize_options(self)
        self.set_undefined_options('install', ('root', 'root'))

        if self.root is not None and own_install_dir:
            self.install_dir = change_root(self.root, self.install_dir)

###########################################################################
class Uninstall(Command):
    description = "Remove all installed files"

    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def get_command_name(self):
        return 'uninstall'

    def run(self):
        global INSTALL_LIST

        if not os.path.isfile(INSTALL_LIST):
            self.announce("Unable to uninstall, can't find the file list %s." % INSTALL_LIST)
            return

        # Suck in the file list.
        fhandle = open(INSTALL_LIST,'r')
        file_list = fhandle.readlines()
        fhandle.close()

        # Remove the files first.
        for item in file_list:
            item = item.strip()
            if os.path.isfile(item) or os.path.islink(item):
                self.announce("removing '%s'" % item)
                if not self.dry_run:
                    try:
                        os.remove(item)
                    except OSError as details:
                        self.warn("Could not remove file: %s" % details)
            elif not os.path.isdir(item):
                self.announce("skipping removal of '%s' (does not exist)" % item)

        # Remove the directories.
        file_list.sort()
        file_list.reverse()
        # Starting with the longest paths first.
        for item in file_list:
            item = item.strip()
            if os.path.isdir(item):
                self.announce("removing '%s'" % item)
                if not self.dry_run:
                    try:
                        os.rmdir(item)
                    except OSError as details:
                        self.warn("Could not remove directory: %s" % details)

###########################################################################
class BuildKControlModule(Command):
    description = "Build KControl extensions"

    user_options = [('no-kcontrol',None,"Don't build kcontrol extensions"),
                    ('build-dir=','b', "build directory (where to install from)"),
                    ('python-dir=',None,'Directory containing the Python installation'),
                    ('python-inc-dir=',None,'Directory containing C Header files for Python'),
                    ('kde-inc-dir=',None,'Directory containing C++ header files for KDE'),
                    ('kde-lib-dir=',None,'Directory containing library files for KDE'),
                    ('kde-kcm-lib-dir=',None,'Directory for KDE kcm library files'),
                    ('tqt-inc-dir=',None,'Directory containing C++ header files for TQt'),
                    ('tqt-lib-dir=',None,'Directory containing library files for TQt'),
                    ('sip-dir=',None,'Directory containing the sip library files'),
                    ('clib=',None,'gcc library and path'),
                    ('pytqt-dir=',None,'PyTQt module directory'),
                    ('pytde-dir=',None,'PyTDE module directory'),
                    ('data-dir=',None,'installation directory for data (script) files')]

    def initialize_options(self):
        self.no_kcontrol = 0
        self.build_dir = None
        self.python_inc_dir = None
        self.python_dir = None
        self.kde_inc_dir = None
        self.kde_lib_dir = None
        self.kde_kcm_lib_dir = None
        self.tqt_inc_dir = None
        self.tqt_lib_dir = None
        self.sip_dir = None
        self.clib = None
        self.pytqt_dir = None
        self.pytde_dir = None
        self.data_dir = None

    def finalize_options(self):
        if self.no_kcontrol==0:
            self.set_undefined_options('install', ('build_base', 'build_dir'),('install_application_data','data_dir'))

            install = self.get_finalized_command('install')
            self.install_prefix = ask_kde_config('--prefix').strip()

            # KDE inc dir: find it!
            if self.kde_inc_dir is None:
                canidatepaths = []
                tdedir = os.getenv("TDEDIR")
                if tdedir!=None:
                    canidatepaths.append(os.path.join(tdedir,"include"))
                canidatepaths.append(os.path.join(install.prefix,"include"))
                canidatepaths.append(os.path.join(self.install_prefix,'include'))
                canidatepaths.append(os.path.join(self.install_prefix,'include','tde'))
                self.kde_inc_dir = FindFileInPaths('tdeapplication.h',canidatepaths)
            if self.kde_inc_dir is None:
                raise SystemExit("Failed to find the TDE header file directory.")
            if FindFileInPaths('tdeapplication.h',[self.kde_inc_dir]) is None:
                raise SystemExit("Failed to find TDE header files in: %s" % self.kde_inc_dir)
            self.announce("Using %s for KDE header files" % self.kde_inc_dir)

            # KDE lib dir
            if self.kde_lib_dir is None:
                self.kde_lib_dir = os.path.join(self.install_prefix,"lib")
            self.announce("Using %s for KDE library files" % self.kde_lib_dir)

            # KDE KCM lib dir
            if self.kde_kcm_lib_dir is None:
                self.kde_kcm_lib_dir = os.path.join(self.kde_lib_dir,"trinity")
            if FindFileInPaths('*kcm*.so',[self.kde_kcm_lib_dir]) is None:
                raise SystemExit("Failed to find TDE KCM files in: %s" % self.kde_kcm_lib_dir)
            self.announce("Using %s for KDE KCM library files" % self.kde_kcm_lib_dir)

            # TQt inc dir
            if self.tqt_inc_dir is None:
                canidatepaths = []
                tqtinc = os.getenv("TQTINC")
                if tqtinc != None:
                    canidatepaths.append(tqtinc)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,"include"))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/include"))
                canidatepaths.append(os.path.join(install.prefix,"include"))
                canidatepaths.append("/opt/tqt3/include")
                canidatepaths.append("/usr/include/tqt3")
                self.tqt_inc_dir = FindFileInPaths('ntqstring.h',canidatepaths)
            if self.tqt_inc_dir is None:
                self.tqt_inc_dir = FindFileInPaths('tqstring.h',canidatepaths)
            if self.tqt_inc_dir is None:
                raise SystemExit("Failed to find the TQt header file directory")
            if FindFileInPaths('ntqstring.h',[self.tqt_inc_dir]) is None:
                if FindFileInPaths('tqstring.h',[self.tqt_inc_dir]) is None:
                    raise SystemExit("Failed to find TQt header files in: %s" % self.tqt_inc_dir)
            self.announce("Using %s for TQt header files" % self.tqt_inc_dir)

            # TQt lib dir
            if self.tqt_lib_dir is None:
                canidatepaths = []
                tqtlib = os.getenv("TQTLIB")
                if tqtlib != None:
                    canidatepaths.append(tqtlib)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,get_libdir_name()))
                canidatepaths.append("/opt/tqt3/"+get_libdir_name())
                canidatepaths.append("/opt/tqt/"+get_libdir_name())
                canidatepaths.append("/opt/tqt/lib/"+get_libdir_name())
                canidatepaths.append("/opt/tqt3/lib/"+get_libdir_name())
                self.tqt_lib_dir = FindFileInPaths('libtqt*',canidatepaths)
            if self.tqt_lib_dir is None:
                canidatepaths = []
                tqtlib = os.getenv("TQTLIB")
                if tqtlib != None:
                    canidatepaths.append(tqtlib)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,get_libdir_name()))
                canidatepaths.append("/opt/tqt3/"+get_libdir_name())
                canidatepaths.append("/opt/tqt3/lib/"+get_libdir_name())
                self.tqt_lib_dir = FindFileInPaths('libtqt*',canidatepaths)
            if self.tqt_lib_dir is None:
                raise SystemExit("Failed to find TQt library files")
            self.announce("Using %s for TQt library files" % self.tqt_lib_dir)

            # Python dir
            if self.python_dir is None:
                self.python_dir = sysconfig.get_path('platstdlib')
            self.announce("Using %s for the python directory" % self.python_dir)

            # Python include dir.
            if self.python_inc_dir is None:
                # Find the Python include directory.
                self.python_inc_dir = sysconfig.get_path('include')
            self.announce("Using %s for Python header files" % self.python_inc_dir)

            # PyTQt dir
            if self.pytqt_dir is None:
                pytqt_dirs = sys.path
                pytqt_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.pytqt_dir = FindFileInPaths("PyTQt/tqt*.so", pytqt_dirs)
                if not self.pytqt_dir is None:
                    self.pytqt_dir = os.path.join(self.pytqt_dir, 'PyTQt')
            if (FindFileInPaths("tqt*", self.pytqt_dir) is None):
                raise SystemExit("Failed to find the PyTQt directory: %s" % self.pytqt_dir)
            self.announce("Using %s for PyTQt modules" % self.pytqt_dir)

            # PyTDE dir
            if self.pytde_dir is None:
                pytde_dirs = sys.path
                pytde_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.pytde_dir = FindFileInPaths("tdecore*.so", pytde_dirs)
            if (FindFileInPaths("tdecore*", self.pytde_dir) is None):
                raise SystemExit("Failed to find the PyTDE directory: %s" % self.pytde_dir)
            self.announce("Using %s for PyTDE modules" % self.pytde_dir)

            # Sip dir
            if self.sip_dir is None:
                sip_dirs = sys.path
                sip_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.sip_dir = FindFileInPaths("sip_tqt*.so", sip_dirs)
            if (FindFileInPaths("sip_tqt*", [self.sip_dir]) is None):
                raise SystemExit("Failed to find sip-tqt directory: %s" % self.sip_dir)
            self.announce("Using %s for sip-tqt files" % self.sip_dir)

            # Find the C library (libgcc, libgcc_s or some other variation).
            if self.clib is None:
                canidatepaths = ["/usr/"+get_libdir_name(), "/usr/local/"+get_libdir_name(), "/usr/lib" ]
                self.clib = FindFileInPaths("libgcc*.so",canidatepaths)
                if self.clib!=None:
                    self.clib = glob.glob(os.path.join(self.clib,'libgcc*.so'))[0]
                else:
                    self.clib = FindFileInPaths("libgcc*.a",canidatepaths)
                    if self.clib!=None:
                        self.clib = glob.glob(os.path.join(self.clib,'libgcc*.a'))[0]
            if self.clib is None:
                raise SystemExit("tdedistutils.py (1): Failed to find a suitable libgcc library")
            self.announce("Using %s for clib" % self.clib)

            # Make a list of places to look for python .so modules
            self.python_sub_dirs = sysconfig.get_config_var("LIBSUBDIRS").split()
            base = sysconfig.get_config_var("LIBPL")
            self.python_sub_dirs = [ os.path.join(base,item) for item in self.python_sub_dirs ]
            self.python_sub_dirs.append(base)

    def get_command_name(self):
        return 'build_kcm'

    def run(self):
        if self.no_kcontrol:
            self.announce("Skipping KControl modules")
            return

        if not os.path.isdir(self.build_dir):
            os.mkdir(self.build_dir)

        for moduletuple in self.distribution.kcontrol_modules:
            self.announce("Building KControl module from desktop file %s." % moduletuple[0])

            # Read the desktop file
            factoryfunction = None
            libraryname = None
            cmodulecategory = None
            try:
                fhandle = open(moduletuple[0], 'r', encoding='utf-8')
                for line in fhandle.readlines():
                    parts = line.strip().split('=')
                    try:
                        if parts[0]=="X-TDE-Library":
                            libraryname = parts[1]
                        elif parts[0]=="Exec":
                            shellcmd = parts[1].split()
                            modulepath = shellcmd[-1]
                            if '/' in modulepath:
                                cmodulecategory = os.path.dirname(modulepath)
                            else:
                                cmodulecategory = ""
                        elif parts[0]=="X-TDE-FactoryName":
                            factoryfunction = 'create_'+parts[1]
                    except IndexError:
                        pass
                fhandle.close()
            except IOError:
                raise SystemExit("Failed to find kcontrol desktop file: %s" % moduletuple[0])

            # Sanity check.
            if factoryfunction is None:
                raise SystemExit("Failed to find factory name (Was there a X-TDE-FactoryName entry in the desktop file?)")
            if libraryname is None:
                raise SystemExit("Failed to find library name (Was there a X-TDE-Library entry in the desktop file?)")
            if cmodulecategory is None:
                raise SystemExit("Failed to find the kcontrol category name (Was there a Exec entry in the desktop file?)")

            modulename = os.path.basename(moduletuple[1])
            if modulename.endswith('.py'):
                modulename = modulename[:-3]
            desktopfilename = moduletuple[0]

            stub_cpp_name = 'kcm_'+libraryname+'.cpp'
            stub_so_name = 'kcm_'+libraryname+'.so'
            stub_la_name = 'kcm_'+libraryname+'.la'
            python_version = '%i.%i' % (sys.version_info[0],sys.version_info[1])

            # Build the 'stub' code.
            cppcode = self.cpptemplate % {"moduledir": self.data_dir,
                                            "extramodule": os.getenv("EXTRA_MODULE_DIR"),
                                            "modulename": modulename,
                                            "factoryfunction": factoryfunction,
                                            "python_version": python_version}

            # Put it on disk.
            cppfile = os.path.join(os.path.dirname(moduletuple[0]),stub_cpp_name)
            try:
                fhandle = open(cppfile,'w')
                fhandle.write(cppcode)
                fhandle.close()
            except IOError:
                raise SystemExit("Could not write the C++ stub: %s" % cppfile)

            # Compile the stub library.
            cmdlist = ['libtool']

            # Couldn't get it to pass without this ...
            cmdlist.append("--mode=compile")
            cmdlist.append("--tag=CXX")

            # Find the compiler flags and options
            # CXX is empty on some Systems, let's do it 'the hard way'.
            # FIXME :: get CXX from make.conf for Gentoo.
            if len(sysconfig.get_config_var("CXX").split()) >= 2:
                cmdlist.extend(sysconfig.get_config_var("CXX").split())
            else:
                cmdlist.extend(['g++', '-pthread'])

            #cmdlist.extend(sysconfig.get_config_var("CXX").split())

            # cc_flags
            cmdlist.append("-c")
            cmdlist.append("-g")

            # The 4 is randomly chosen!
            # FIXME :: get CFLAGS from make.conf for Gentoo.
            if len(sysconfig.get_config_var("CFLAGS").split()) >=4:
                cmdlist.extend(sysconfig.get_config_var("CFLAGS").split())
            else:
                # On Gentoo systems, CFLAGS are not in the environment.
                raw = os.popen('emerge info 2> /dev/null|grep CFLAGS')
                lines = raw.readlines()
                if len(lines):
                    cflags = lines[0].split('"')[1].split()
                    print("Got CFLAGS from emerge info.")
                    cmdlist.extend(cflags)
                else:
                    # Still no CFLAGS found, use these ...
                    cmdlist.extend(['-fno-strict-aliasing', '-DNDEBUG', '-g', '-O3', '-Wall', '-Wstrict-prototypes'])

            #sysconfig.get_config_var("CFLAGS").split()
            # includes
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEDIR"))
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEDIR"))
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEPY"))
            cmdlist.append("-I" + self.python_inc_dir)
            cmdlist.append("-I" + self.kde_inc_dir)
            cmdlist.append("-I" + self.kde_inc_dir + "/tde")
            cmdlist.append("-I" + self.tqt_inc_dir)
            cmdlist.append("-I/usr/include/tqt")
            cmdlist.append("-I.")
            # input
            cmdlist.append(cppfile)
            # output
            outputfile = os.path.join(self.build_dir,libraryname+'.lo')
            cmdlist.append("-o")
            cmdlist.append(outputfile)
            spawn(cmdlist) # Execute!!!
            print()

            # Link the resulting object file to create a shared library.
            cmdlist = ['libtool']
            cmdlist.append("--mode=link")
            cmdlist.append("--tag=LD")

            # Grab the linker command name
            cmdlist.append(sysconfig.get_config_var("LDSHARED").split()[0])
            # link_flags
            cmdlist.append("-module")
            cmdlist.append("-avoid-version")
            cmdlist.append("-shared")
            cmdlist.append("-export-dynamic")
            # object
            cmdlist.append(outputfile)
            cmdlist.append("-rpath"); cmdlist.append(self.kde_kcm_lib_dir)
            cmdlist.append("-o"); cmdlist.append(os.path.join(self.build_dir,stub_la_name))
            # Link libs
            linklist = []
            linklist.append("-lpython%s%s" % (python_version, sys.abiflags))
            linklist.extend(sysconfig.get_config_var("LIBS").split())

            # FIXME I doubt half of these libraries need to be here.
            linklist.append(self.sip_dir+"/sip_tqt.so")
            # PyTQt libs
            linklist.append(self.pytqt_dir+"/tqt.so")
            # PyTDE libs
            linklist.append(self.pytde_dir+"/tdecore.so")
            linklist.append(self.pytde_dir+"/tdeui.so")

#            linklist.append("-L"+self.sip_dir); linklist.append("-lsip_tqt")
#            # PyTQt libs
#            linklist.append("-L"+self.pytqt_dir); linklist.append("-lqtcmodule")
#            # PyTDE libs
#            linklist.append("-L"+self.pytde_dir); linklist.append("-ltdecorecmodule"); linklist.append("-ltdeuicmodule")

            linklist.append("-L"+self.kde_lib_dir); linklist.append("-L/opt/trinity/lib"); linklist.append("-ltdecore"); linklist.append("-lpythonize")
            linklist.append("-L"+self.tqt_lib_dir); linklist.append("-ltqt-mt")
            linklist.append("-lm")
            linklist.append("-lc")
            linklist.append(self.clib)

            linklist.append("-R"); linklist.append(self.python_dir)
            linklist.append("-R"); linklist.append(self.tqt_lib_dir)
            linklist.append("-R"); linklist.append(self.sip_dir)
            linklist.append("-R"); linklist.append(self.pytqt_dir)
            linklist.append("-R"); linklist.append(self.pytde_dir)
            linklist.append("-R"); linklist.append(self.kde_lib_dir)

            cmdlist.extend(linklist)
            spawn(cmdlist) # Execute!!
            print()

    cpptemplate = r"""
/*
 * pykcm_launcher.cpp
 *
 * Launch Control Centre modules written in Python using an embedded Python
 * interpreter.
 * Based on David Boddie's PyTDE-components.
 */

// pythonize.h must be included first.
#include <pythonize.h>
#include <tdecmodule.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <klibloader.h>
#include <kstandarddirs.h>
#include <ksimpleconfig.h>
#include <tqstring.h>
#include <sip-tqt.h>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif  // _GNU_SOURCE
#include <dlfcn.h>

#define MODULE_DIR "%(moduledir)s"
#define EXTRA_MODULE_DIR "%(extramodule)s"
#define MODULE_NAME "%(modulename)s"
#define FACTORY "%(factoryfunction)s"
#define CPP_FACTORY %(factoryfunction)s
#define debug 1

static TDECModule *report_error(const char *msg) {
    if (debug) printf ("error: %%s\n", msg);
    return NULL;
}

static TDECModule* return_instance( TQWidget *parent, const char *name ) {
    TDECModule* tdecmodule;
    PyObject *pyTDECModuleTuple;
    PyObject *pyTDECModule;
    Pythonize *pyize;  // Pythonize object to manage the Python interpreter.

    // Try to determine what py script we're loading. Note that "name"
    // typically appears to be NULL.
    TQString script(MODULE_NAME);

    // Reload this module, but this time tell the runtime linker to make the
    // symbols global and available for later loaded libraries/module.
    Dl_info info;
    if (!dladdr((const void *)(&return_instance), &info) || !info.dli_fname ||  !dlopen(info.dli_fname, RTLD_GLOBAL|RTLD_NOW)) {
        return report_error ("***Unable to export symbols\n");
    }

    // Start the interpreter.
    pyize = initialize();
    if (!pyize) {
        return report_error ("***Failed to start interpreter\n");
    }

    // Add the path to the python script to the interpreter search path.
    TQString path = TQString(MODULE_DIR);
    if(path == TQString::null) {
        return report_error ("***Failed to locate script path");
    }
    if(!pyize->appendToSysPath (path.latin1 ())) {
        return report_error ("***Failed to set sys.path\n");
    }

    // Add the extra path to the python script to the interpreter search path.
    TQString extrapath = TQString(EXTRA_MODULE_DIR);
    if(!pyize->appendToSysPath (extrapath.latin1 ())) {
      return report_error ("***Failed to set extra sys.path\n");
    }

    // Load the Python script.
    PyObject *pyModule = pyize->importModule ((char *)script.latin1 ());
    if(!pyModule) {
        PyErr_Print();
        return report_error ("***failed to import module\n");
    }

    // Inject a helper function
    TQString bridge = TQString("import sip_tqt\n"
                            "from PyTQt import tqt\n"
                            "def kcontrol_bridge_" FACTORY "(parent,name):\n"
                             "    if parent!=0:\n"
                             "        wparent = sip_tqt.wrapinstance(parent,tqt.TQWidget)\n"
                             "    else:\n"
                             "        wparent = None\n"
                             "    inst = " FACTORY "(wparent, name)\n"
                             "    return (inst,sip_tqt.unwrapinstance(inst))\n");
    PyRun_String(bridge.latin1(),Py_file_input,PyModule_GetDict(pyModule),PyModule_GetDict(pyModule));

    // Get the Python module's factory function.
    PyObject *kcmFactory = pyize->getNewObjectRef(pyModule, "kcontrol_bridge_" FACTORY);
    if(!kcmFactory) {
        return report_error ("***failed to find module factory\n");
    }

    // Call the factory function. Set up the args.
    PyObject *pyParent = PyLong_FromVoidPtr(parent);
    PyObject *pyName = PyBytes_FromString(MODULE_NAME);
        // Using NN here is effect gives our references to the arguement away.
    PyObject *args = Py_BuildValue ("NN", pyParent, pyName);
    if(pyName && pyParent && args) {
        // run the factory function
        pyTDECModuleTuple = pyize->runFunction(kcmFactory, args);
        if(!pyTDECModuleTuple) {
            PyErr_Print();
            return report_error ("*** runFunction failure\n;");
        }
    } else {
        return report_error ("***failed to create args\n");
    }
    // cleanup a bit
    pyize->decref(args);
    pyize->decref(kcmFactory);

    // Stop this from getting garbage collected.
    Py_INCREF(PyTuple_GET_ITEM(pyTDECModuleTuple,0));

    // convert the TDECModule PyObject to a real C++ TDECModule *.
    pyTDECModule = PyTuple_GET_ITEM(pyTDECModuleTuple,1);
    tdecmodule = (TDECModule *)PyLong_AsVoidPtr(pyTDECModule);
    if(!tdecmodule) {
        return report_error ("***failed sip-tqt conversion to C++ pointer\n");
    }
    pyize->decref(pyTDECModuleTuple);

    // PyTDE can't run the module without this - Pythonize
    // grabs the lock at initialization and we have to give
    // it back before exiting. At this point, we no longer need
    // it.
    //pyize->releaseLock ();

    // take care of any translation info
    TDEGlobal::locale()->insertCatalogue(script);

    // Return the pointer to our new TDECModule
    return tdecmodule;
}

extern "C" {
    // Factory function that kcontrol will call.
    TDECModule* CPP_FACTORY(TQWidget *parent, const char *name) {
        return return_instance(parent, name);
    }
}
"""

###########################################################################
class InstallKControlModule(Command):
    description = "Install Kcontrol module files"

    user_options = [
        ('install-dir=', 'd', "base directory for installing kcontrol module files"),
        ('install-cmd=', None, "Command to use to install the files"),
        ('xdg-apps-dir=',None,"directory for XDG app files"),
        ('build-dir=','b', "build directory (where to install from)"),
        ('root=', None, "install everything relative to this alternate root directory"),
        ('force', 'f', "force installation (overwrite existing files)"),
        ('skip-build', None, "skip the build steps"),
        ]
    boolean_options = ['force', 'skip-build']

    def initialize_options(self):
        self.build_dir = None
        self.install_dir = None
        self.install_cmd = None
        self.xdg_apps_dir = None
        self.outfiles = []
        self.root = None
        self.force = 0
        self.warn_dir = 1
        self.skip_build = None

    def finalize_options(self):
        own_install_dir = self.install_dir is not None
        own_xdg_apps_dir = self.xdg_apps_dir is not None

        self.set_undefined_options('install',
                                    ('build_base', 'build_dir'),
                                    ('install_kcm', 'install_dir'),
                                    ('install_xdg_apps','xdg_apps_dir'),
                                    ('root', 'root'),
                                    ('force', 'force'),
                                    ('skip_build', 'skip_build'),
                                    ('install_cmd', 'install_cmd')
                                    )

        if own_install_dir and self.root is not None:
            self.install_dir = change_root(self.root,self.installdir)
        if own_xdg_apps_dir and self.root is not None:
            self.xdg_apps_dir = change_root(self.root,self.xdg_apps_dir)

    def get_command_name(self):
        return 'install_kcm'

    def run(self):
        if not self.skip_build:
            self.run_command('build_kcm')

        self.announce("Installing Kcontrol module files...")

        for moduletuple in self.distribution.kcontrol_modules:
            self.announce("Building KControl module from desktop file %s." % moduletuple[0])

            # Read the desktop file
            libraryname = None
            cmodulecategory = None
            try:
                fhandle = open(moduletuple[0], 'r', encoding='utf-8')
                for line in fhandle.readlines():
                    parts = line.strip().split('=')
                    try:
                        if parts[0]=="X-TDE-Library":
                            libraryname = parts[1]
                        elif parts[0]=="Exec":
                            shellcmd = parts[1].split()
                            modulepath = shellcmd[-1]
                            if '/' in modulepath:
                                cmodulecategory = os.path.dirname(modulepath)
                            else:
                                cmodulecategory = ""
                    except IndexError:
                        pass
                fhandle.close()
            except IOError:
                raise SystemExit("Failed to find kcontrol desktop file: %s" % moduletuple[0])

            if libraryname is None:
                raise SystemExit("Failed to find library name (Was there a X-TDE-Library entry in the desktop file?)")
            if cmodulecategory is None:
                raise SystemExit("Failed to find the kcontrol category name (Was there a Exec entry in the desktop file?)")

            desktopfilename = moduletuple[0]
            self.outfiles.extend(self.mkpath(self.xdg_apps_dir))
            desktopfile_dest = os.path.join(self.xdg_apps_dir,os.path.basename(desktopfilename))
            self.copy_file(desktopfilename, desktopfile_dest)

            stub_la_name = 'kcm_'+libraryname+'.la'

            self.outfiles.extend(self.mkpath(self.install_dir))

            # Install the library.
            cmdlist = ['libtool']
            cmdlist.append("--mode=install")
            cmdlist.append(self.install_cmd)
            cmdlist.append("-c")
            cmdlist.append(os.path.join(self.build_dir,stub_la_name))
            cmdlist.append(os.path.join(self.install_dir,stub_la_name))
            spawn(cmdlist) # Execute!!
            print()

            self.outfiles = [os.path.join(self.install_dir,os.path.basename(file)) for file in glob.glob(os.path.join(self.build_dir,'.libs','kcm_'+libraryname+'*'))]
            self.outfiles.append(desktopfile_dest)

        self.announce("Done installing Kcontrol module files.")

    def get_outputs(self):
        return self.outfiles or []

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name

###########################################################################
class BuildDocbookHTML(Command):
    description = "Build Docbook HTML documentation"

    user_options = [('meinproc-exe=',None,"Path to the meinproc executable")]

    def initialize_options(self):
        self.html_prefix = None
        self.meinproc_exe = None

    def finalize_options(self):
        self.set_undefined_options('build', ('meinproc_exe', 'meinproc_exe') )

    def get_command_name(self):
        return 'build_docbook'

    def run(self):
        for docbook_tuple in self.distribution.docbooks:
            input_dir = docbook_tuple[0]
            language_code = docbook_tuple[1]

            self.announce("Building Docbook documentation from directory %s." % input_dir)

            indexdoc_file_name = os.path.join(input_dir,'index.docbook')
            if not os.path.exists(indexdoc_file_name):
                raise SystemExit("File %s is missing." % indexdoc_file_name)

            cwd = os.getcwd()
            os.chdir(input_dir)
            try:
                spawn([self.meinproc_exe,"--check","--cache",'index.cache.bz2', 'index.docbook'])
                spawn([self.meinproc_exe, 'index.docbook'])
            finally:
                os.chdir(cwd)

###########################################################################
class InstallDocbookHTML(Command):
    description = "Install Docbook HTML files"

    user_options = [
        ('install-dir=', 'd',"base directory for installing docbook HTML files"),
        ('root=', None, "install everything relative to this alternate root directory"),
        ('force', 'f', "force installation (overwrite existing files)"),
        ('skip-build', None, "skip the build steps"),
        ]
    boolean_options = ['force', 'skip-build']

    def initialize_options(self):
        self.install_dir = None
        self.outfiles = []
        self.root = None
        self.force = 0
        self.warn_dir = 1
        self.skip_build = None

    def finalize_options(self):
        own_install_dir = self.install_dir is not None

        self.set_undefined_options('install',
                                   ('install_html', 'install_dir'),
                                   ('root', 'root'),
                                   ('force', 'force'),
                                   ('skip_build', 'skip_build'),
                                  )

        if own_install_dir and self.root is not None:
            self.install_dir = change_root(self.root,self.installdir)

    def get_command_name(self):
        return 'install_html'

    def run(self):
        if not self.skip_build:
            self.run_command('build_html')

        self.announce("Installing HTML files...")
        counter = 0
        for docbook_tuple in self.distribution.docbooks:
            input_dir = docbook_tuple[0]
            language_code = docbook_tuple[1]

            self.announce("Install Docbook documentation from directory %s." % input_dir)
            source_file = os.path.join(input_dir,'index.cache.bz2')
            if not os.path.exists(source_file):
                raise SystemExit("File %s is missing." % source_file)

            dest_path = os.path.join(self.install_dir, language_code, self.distribution.metadata.name)
            self.outfiles.extend(self.mkpath(dest_path))
            dest_file = os.path.join(dest_path,'index.cache.bz2')

            self.copy_file(source_file, dest_file)
            self.outfiles.append(dest_file)
            counter += 1

            # Also install any lose HTML files.
            for source_file in glob.glob(os.path.join(input_dir,'*.html')):
                htmlfile = os.path.basename(source_file)
                dest_file = os.path.join(dest_path,htmlfile)
                self.copy_file(source_file, dest_file)
                self.outfiles.append(dest_file)
                counter += 1

            if len(docbook_tuple)==3:
                extra_files = docbook_tuple[2]
                for file in extra_files:
                    source_file = os.path.join(input_dir,file)
                    dest_file = os.path.join(self.install_dir, language_code, self.distribution.metadata.name,file)
                    self.copy_file(source_file, dest_file)
                    self.outfiles.append(dest_file)
                    counter += 1

        self.announce("Done installing %i HTML files." % counter)

    def get_outputs(self):
        return self.outfiles or []

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name

###########################################################################
class UpdateI18NMessages(Command):
    description = "Extract and update messages for translation"

    user_options = [('xgettext-exe=',None,'Full path to the xgetext executable'),\
                    ('kde-pot=',None,'Location of the the KDE pot file'),\
                    ('msgmerge-exe=',None,'Full path to the msgmerge executable')]

    def initialize_options(self):
        self.xgettext_exe = None
        self.msgmerge_exe = None
        self.kde_pot = None

    def finalize_options(self):
        if self.xgettext_exe is None:
            install = self.get_finalized_command('install')

            canidate_paths = []
            if install.user_supplied_kde_prefix:
                canidate_paths.append(os.path.join(install.kde_prefix,'bin'))

            self.announce("Detecting xgettext...")
            canidate_paths.append(ask_kde_config('--install exe --expandvars').strip())
            self.xgettext_exe = FindExeOnPath('xgettext',canidate_paths)
            if self.xgettext_exe is None:
                raise SystemExit("Unable to find 'xgettext'.")
            self.announce("  ...xgettext found at %s" % self.xgettext_exe)

        if self.msgmerge_exe is None:
            install = self.get_finalized_command('install')

            canidate_paths = []
            if install.user_supplied_kde_prefix:
                canidate_paths.append(os.path.join(install.kde_prefix,'bin'))

            self.announce("Detecting msgmerge...")
            canidate_paths.append(ask_kde_config('--install exe --expandvars').strip())
            self.msgmerge_exe = FindExeOnPath('msgmerge',canidate_paths)
            if self.msgmerge_exe is None:
                raise SystemExit("Unable to find 'xgettext'.")
            self.announce("  ...msgmerge found at %s" % self.msgmerge_exe)

        if self.kde_pot is None:
            self.announce("Detecting kde.pot...")
            canidatepaths = []
            tdedir = os.getenv("TDEDIR")
            if tdedir!=None:
                canidatepaths.append(os.path.join(tdedir,"include"))
            install = self.get_finalized_command('install')
            canidatepaths.append(os.path.join(install.kde_prefix,"include"))
            canidatepaths.append('/opt/trinity/include')
            canidatepaths.append('/opt/kde/include')
            kde_pot_dir = FindFileInPaths('kde.pot',canidatepaths)

            if kde_pot_dir is None:
                raise SystemExit("Failed to find the kde.pot file.")

            self.kde_pot = os.path.join(kde_pot_dir,'kde.pot')
            self.announce("   ...kde.pot found at %s" % self.kde_pot)

    def get_command_name(self):
        return 'update_messages'

    def run(self):
        if self.distribution.i18n is None: return

        self.announce("Extracting and merging i18n messages...")
        po_dir = self.distribution.i18n[0]

        # FIXME : .rc and .ui files
        input_files = []

        # Compile any UI files
        for dir in self.distribution.i18n[1]:
            for file in glob.glob(os.path.join(dir,'*.ui')):
                tqtuicompiler.UpdateUI(file,kde=True)

        # Fetch all of the python files.
        for dir in self.distribution.i18n[1]:
            input_files.extend(glob.glob(os.path.join(dir,'*.py')))

        target_pot = os.path.join(po_dir,self.distribution.metadata.name+".pot")

        cmd = [self.xgettext_exe, '-o', target_pot, '-ki18n', '-ktr2i18n', \
                '-kI18N_NOOP', '-ktranslate', '-kaliasLocale','-x',self.kde_pot]
        cmd.extend(input_files)
        spawn(cmd)

        for po_file in glob.glob(os.path.join(po_dir,'*.po')):
            temp_po = po_file + '.temp'
            cmd = [self.msgmerge_exe,'-q','-o',temp_po,po_file,target_pot]
            spawn(cmd)
            os.rename(temp_po,po_file)

        self.announce("Finished with i18n messages.")

###########################################################################
class BuildI18NMessages(Command):
    description = "Build i18n messages"

    user_options = [('msgfmt-exe=',None,'Path to the msgfmt executable')]

    def initialize_options(self):
        self.msgfmt_exe = None

    def finalize_options(self):
        self.set_undefined_options('build', ('msgfmt_exe', 'msgfmt_exe'))

    def get_command_name(self):
        return 'build_messages'

    def run(self):
        if self.distribution.i18n is None: return

        self.announce("Building i18n messages...")
        po_dir = self.distribution.i18n[0]

        i = 0
        for po_file in [file for file in os.listdir(po_dir) if file.endswith('.po')]:
            source = os.path.join(po_dir,po_file)
            target = source[:-3]+'.gmo'
            cmd = [self.msgfmt_exe,'-o',target, source]
            spawn(cmd)
            i += 1
        self.announce("Done building %i i18n messages." % i)

###########################################################################
class InstallI18NMessages(Command):
    description = "Install messages"

    user_options = [
        ('install-dir=', 'd',"base directory for installing message files (default: installation base dir)"),
        ('root=', None, "install everything relative to this alternate root directory"),
        ('force', 'f', "force installation (overwrite existing files)"),
        ('skip-build', None, "skip the build steps"),
        ]

    boolean_options = ['force', 'skip-build']

    def initialize_options(self):
        self.install_dir = None
        self.outfiles = []
        self.root = None
        self.force = 0
        self.warn_dir = 1
        self.skip_build = None

    def finalize_options(self):
        own_install_dir = self.install_dir is not None

        self.set_undefined_options('install',
                                   ('install_messages', 'install_dir'),
                                   ('root', 'root'),
                                   ('force', 'force'),
                                   ('skip_build', 'skip_build'),
                                  )

        if own_install_dir and self.root is not None:
            self.install_dir = change_root(self.root,self.installdir)

    def get_command_name(self):
        return 'install_messages'

    def run(self):
        if not self.skip_build:
            self.run_command('build_messages')

        self.announce("Installing i18n messages...")
        po_dir = self.distribution.i18n[0]

        counter = 0
        for po_file in os.listdir(po_dir):
            if po_file.endswith('.po'):
                source_file = os.path.join(po_dir,po_file[:-3]) + '.gmo'

                # Setup installation of the translation file.
                dest_path = os.path.join(self.install_dir, po_file[:-3],'LC_MESSAGES')
                self.outfiles.extend(self.mkpath(dest_path))
                dest_file = os.path.join(dest_path, self.distribution.metadata.name+'.mo')

                self.copy_file(source_file, dest_file)
                self.outfiles.append(dest_file)
                counter += 1
        self.announce("Done installing %i i18n messages." % counter)

    def get_outputs(self):
        return self.outfiles

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name


###########################################################################
class BuildTdeioslave(Command):
    description = "Build Tdeioslaves"

    user_options = [('no-tdeioslave',None,"Don't build tdeioslaves"),
                    ('build-dir=','b', "build directory (where to install from)"),
                    ('python-dir=',None,'Directory containing the Python installation'),
                    ('python-inc-dir=',None,'Directory containing C Header files for Python'),
                    ('kde-inc-dir=',None,'Directory containing C++ header files for KDE'),
                    ('kde-lib-dir=',None,'Directory containing library files for KDE'),
                    ('kde-tdeioslave-lib-dir=',None,'Directory for KDE tdeioslave library files'),
                    ('kde-protocol-dir=',None,'Directory for KDE tdeioslave protocol files'),
                    ('tqt-inc-dir=',None,'Directory containing C++ header files for TQt'),
                    ('tqt-lib-dir=',None,'Directory containing library files for TQt'),
                    ('sip-dir=','/usr/lib/pyshared/python*','Directory containing the sip library files'),
                    ('clib=',None,'gcc library and path'),
                    ('pytqt-dir=','/usr/lib/pyshared/python*','PyTQt module directory'),
                    ('pytde-dir=',None,'PyTDE module directory'),
                    ('data-dir=',None,'installation directory for data (script) files')]

    def initialize_options(self):
        self.no_tdeioslave = 0
        self.build_dir = None
        self.python_inc_dir = None
        self.python_dir = None
        self.kde_inc_dir = None
        self.kde_lib_dir = None
        self.kde_tdeioslave_lib_dir = None
        self.kde_protocol_dir = None
        self.tqt_inc_dir = None
        self.tqt_lib_dir = None
        self.sip_dir = None
        self.clib = None
        self.pytqt_dir = None
        self.pytde_dir = None
        self.data_dir = None

    def finalize_options(self):
        if self.no_tdeioslave==0:
            self.set_undefined_options('install', ('build_base', 'build_dir'),('install_application_data','data_dir'))

            install = self.get_finalized_command('install')
            self.install_prefix = ask_kde_config('--prefix').strip()

            # KDE inc dir: find it!
            if self.kde_inc_dir is None:
                canidatepaths = []
                tdedir = os.getenv("TDEDIR")
                if tdedir!=None:
                    canidatepaths.append(os.path.join(tdedir,"include"))
                canidatepaths.append(os.path.join(install.prefix,"include"))
                canidatepaths.append(os.path.join(self.install_prefix,'include'))
                canidatepaths.append(os.path.join(self.install_prefix,'include','tde'))
                self.kde_inc_dir = FindFileInPaths('tdeapplication.h',canidatepaths)
            if self.kde_inc_dir is None:
                raise SystemExit("Failed to find the TDE header file directory.")
            if FindFileInPaths('tdeapplication.h',[self.kde_inc_dir]) is None:
                raise SystemExit("Failed to find TDE header files in: %s" % self.kde_inc_dir)
            self.announce("Using %s for KDE header files" % self.kde_inc_dir)

            # KDE lib dir
            if self.kde_lib_dir is None:
                self.kde_lib_dir = os.path.join(self.install_prefix,"lib")
            self.announce("Using %s for KDE library files" % self.kde_lib_dir)

            # KDE tdeioslave lib dir
            if self.kde_tdeioslave_lib_dir is None:
                self.kde_tdeioslave_lib_dir = os.path.join(self.kde_lib_dir,"trinity")
            if FindFileInPaths('tdeio_*.so',[self.kde_tdeioslave_lib_dir]) is None:
                raise SystemExit("Failed to find TDE Tdeioslave library files in: %s" % self.kde_tdeioslave_lib_dir)
            self.announce("Using %s for KDE Tdeioslave library files" % self.kde_tdeioslave_lib_dir)

            # TQt inc dir
            if self.tqt_inc_dir is None:
                canidatepaths = []
                tqtinc = os.getenv("TQTINC")
                if tqtinc != None:
                    canidatepaths.append(tqtinc)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,"include"))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/include"))
                canidatepaths.append(os.path.join(install.prefix,"include"))
                canidatepaths.append("/opt/tqt3/include")
                canidatepaths.append("/opt/tqt3/lib/include")
                self.tqt_inc_dir = FindFileInPaths('ntqstring.h',canidatepaths)
            if self.tqt_inc_dir is None:
                self.tqt_inc_dir = FindFileInPaths('tqstring.h',canidatepaths)
            if self.tqt_inc_dir is None:
                raise SystemExit("Failed to find the TQt header file directory")
            if FindFileInPaths('ntqstring.h',[self.tqt_inc_dir]) is None:
                if FindFileInPaths('tqstring.h',[self.tqt_inc_dir]) is None:
                    raise SystemExit("Failed to find TQt header files in: %s" % self.tqt_inc_dir)
            self.announce("Using %s for TQt header files" % self.tqt_inc_dir)

            # TQt lib dir
            if self.tqt_lib_dir is None:
                canidatepaths = []
                tqtlib = os.getenv("TQTLIB")
                if tqtlib != None:
                    canidatepaths.append(tqtlib)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,get_libdir_name()))
                canidatepaths.append("/opt/tqt3/"+get_libdir_name())
                canidatepaths.append("/opt/tqt/"+get_libdir_name())
                canidatepaths.append("/opt/tqt/lib/"+get_libdir_name())
                canidatepaths.append("/opt/tqt3/lib/"+get_libdir_name())
                self.tqt_lib_dir = FindFileInPaths('libtqt*',canidatepaths)
            if self.tqt_lib_dir is None:
                canidatepaths = []
                tqtlib = os.getenv("TQTLIB")
                if tqtlib != None:
                    canidatepaths.append(tqtlib)
                tqtdir = os.getenv("TQTDIR")
                if tqtdir != None:
                    canidatepaths.append(os.path.join(tqtdir,get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,"lib/tqt3/"+get_libdir_name()))
                canidatepaths.append(os.path.join(install.prefix,get_libdir_name()))
                canidatepaths.append("/opt/tqt3/"+get_libdir_name())
                canidatepaths.append("/opt/tqt3/lib/"+get_libdir_name())
                self.tqt_lib_dir = FindFileInPaths('libtqt*',canidatepaths)
            if self.tqt_lib_dir is None:
                raise SystemExit("Failed to find TQt library files")
            self.announce("Using %s for TQt library files" % self.tqt_lib_dir)

            # Python dir
            if self.python_dir is None:
                self.python_dir = sysconfig.get_path('platstdlib')
            self.announce("Using %s for the python directory" % self.python_dir)

            # Python include dir.
            if self.python_inc_dir is None:
                # Find the Python include directory.
                self.python_inc_dir = sysconfig.get_path('include')
            self.announce("Using %s for Python header files" % self.python_inc_dir)

            # PyTQt dir
            if self.pytqt_dir is None:
                pytqt_dirs = sys.path
                pytqt_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.pytqt_dir = FindFileInPaths("PyTQt/tqt*.so", pytqt_dirs)
                if not self.pytqt_dir is None:
                    self.pytqt_dir = os.path.join(self.pytqt_dir, 'PyTQt')
            if (FindFileInPaths("tqt*", self.pytqt_dir) is None):
                raise SystemExit("Failed to find the PyTQt directory: %s" % self.pytqt_dir)
            self.announce("Using %s for PyTQt modules" % self.pytqt_dir)

            # PyTDE dir
            if self.pytde_dir is None:
                pytde_dirs = sys.path
                pytde_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.pytde_dir = FindFileInPaths("tdecore*.so", pytde_dirs)
            if (FindFileInPaths("tdecore*", self.pytde_dir) is None):
                raise SystemExit("Failed to find the PyTDE directory: %s" % self.pytde_dir)
            self.announce("Using %s for PyTDE modules" % self.pytde_dir)

            # Sip dir
            if self.sip_dir is None:
                sip_dirs = sys.path
                sip_dirs.insert(0, sysconfig.get_path('platstdlib'))
                self.sip_dir = FindFileInPaths("sip_tqt*.so", sip_dirs)
            if (FindFileInPaths("sip_tqt*", [self.sip_dir]) is None):
                raise SystemExit("Failed to find sip-tqt directory: %s" % self.sip_dir)
            self.announce("Using %s for sip-tqt files" % self.sip_dir)

            # Find the C library (libgcc, libgcc_s or some other variation).
            if self.clib is None:
                canidatepaths = ["/usr/"+get_libdir_name(), "/usr/local/"+get_libdir_name() ]
                self.clib = FindFileInPaths("libgcc*.so",canidatepaths)
                if self.clib!=None:
                    self.clib = glob.glob(os.path.join(self.clib,'libgcc*.so'))[0]
                else:
                    self.clib = FindFileInPaths("libgcc*.a",canidatepaths)
                    if self.clib!=None:
                        self.clib = glob.glob(os.path.join(self.clib,'libgcc*.a'))[0]
            if self.clib is None:
                raise SystemExit("tdedistutils.py (2): Failed to find a suitable libgcc library")
            self.announce("Using %s for clib" % self.clib)

            # Make a list of places to look for python .so modules
            self.python_sub_dirs = sysconfig.get_config_var("LIBSUBDIRS").split()
            base = sysconfig.get_config_var("LIBPL")
            self.python_sub_dirs = [ os.path.join(base,item) for item in self.python_sub_dirs ]
            self.python_sub_dirs.append(base)

    def get_command_name(self):
        return 'build_tdeioslave'

    def run(self):
        if self.no_tdeioslave:
            self.announce("Skipping TDEIO Slaves")
            return

        if not os.path.isdir(self.build_dir):
            os.mkdir(self.build_dir)

        for moduletuple in self.distribution.tdeioslaves:
            self.announce("Building TDEIO Slave from protocol file %s." % moduletuple[0])

            protocolfilename = moduletuple[0]

            # Read the protocol file
            libraryname = None
            try:
                fhandle = open(protocolfilename,'r')
                for line in fhandle.readlines():
                    parts = line.strip().split('=')
                    try:
                        if parts[0]=="exec":
                            libraryname = parts[1]
                    except IndexError:
                        pass
                fhandle.close()
            except IOError:
                raise SystemExit("Failed to find tdeioslave protocol file: %s" % moduletuple[0])

            # Sanity check.
            if libraryname is None:
                raise SystemExit("Failed to find library name (Was there a exec entry in the protocol file?)")

            modulename = os.path.basename(moduletuple[1])
            if modulename.endswith('.py'):
                modulename = modulename[:-3]

            stub_cpp_name = libraryname+'.cpp'
            stub_so_name = libraryname+'.so'
            stub_la_name = libraryname+'.la'
            python_version = '%i.%i' % (sys.version_info[0],sys.version_info[1])

            # Build the 'stub' code.
            cppcode = self.cpptemplate % {"moduledir": self.data_dir,
                                            "modulename": modulename,
                                            "python_version": python_version}

            # Put it on disk.
            cppfile = os.path.join(os.path.dirname(moduletuple[0]),stub_cpp_name)
            try:
                fhandle = open(cppfile,'w')
                fhandle.write(cppcode)
                fhandle.close()
            except IOError:
                raise SystemExit("Could not write the C++ stub: %s" % cppfile)

            # Compile the stub library.
            cmdlist = ['libtool']

            # Couldn't get it to pass without this ...
            cmdlist.append("--mode=compile")
            cmdlist.append("--tag=CXX")

            # Find the compiler flags and options
            # CXX is empty on some Systems, let's do it 'the hard way'.
            # FIXME :: get CXX from make.conf for Gentoo.
            if len(sysconfig.get_config_var("CXX").split()) >= 2:
                cmdlist.extend(sysconfig.get_config_var("CXX").split())
            else:
                cmdlist.extend(['g++', '-pthread'])

            #cmdlist.extend(sysconfig.get_config_var("CXX").split())

            # cc_flags
            cmdlist.append("-c")
            cmdlist.append("-g")

            # The 4 is randomly chosen!
            # FIXME :: get CFLAGS from make.conf for Gentoo.
            if len(sysconfig.get_config_var("CFLAGS").split()) >=4:
                cmdlist.extend(sysconfig.get_config_var("CFLAGS").split())
            else:
                # On Gentoo systems, CFLAGS are not in the environment.
                raw = os.popen('emerge info 2> /dev/null|grep CFLAGS')
                lines = raw.readlines()
                if len(lines):
                    cflags = lines[0].split('"')[1].split()
                    print("Got CFLAGS from emerge info.")
                    cmdlist.extend(cflags)
                else:
                    # Still no CFLAGS found, use these ...
                    cmdlist.extend(['-fno-strict-aliasing', '-DNDEBUG', '-g', '-O3', '-Wall', '-Wstrict-prototypes'])

            #sysconfig.get_config_var("CFLAGS").split()
            # includes
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEDIR"))
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEDIR"))
            cmdlist.append("-I" + sysconfig.get_config_var("INCLUDEPY"))
            cmdlist.append("-I" + self.python_inc_dir)
            cmdlist.append("-I" + self.kde_inc_dir)
            cmdlist.append("-I" + self.kde_inc_dir + "/tde")
            cmdlist.append("-I" + self.tqt_inc_dir)
            cmdlist.append("-I/usr/include/tqt")
            cmdlist.append("-I.")
            # input
            cmdlist.append(cppfile)
            # output
            outputfile = os.path.join(self.build_dir,libraryname+'.lo')
            cmdlist.append("-o")
            cmdlist.append(outputfile)
            spawn(cmdlist) # Execute!!!
            print()

            # Link the resulting object file to create a shared library.
            cmdlist = ['libtool']
            cmdlist.append("--mode=link")
            cmdlist.append("--tag=LD")

            # Grab the linker command name
            cmdlist.append(sysconfig.get_config_var("LDSHARED").split()[0])
            # link_flags
            cmdlist.append("-module")
            cmdlist.append("-avoid-version")
            cmdlist.append("-shared")
            cmdlist.append("-export-dynamic")
            # object
            cmdlist.append(outputfile)
            cmdlist.append("-rpath"); cmdlist.append(self.kde_tdeioslave_lib_dir)
            cmdlist.append("-o"); cmdlist.append(os.path.join(self.build_dir,stub_la_name))
            # Link libs
            linklist = []
            linklist.append("-lpython%s" % python_version)
            linklist.extend(sysconfig.get_config_var("LIBS").split())

            # FIXME I doubt half of these libraries need to be here.
            linklist.append(self.sip_dir+"/sip_tqt.so")
            # PyTQt libs
            linklist.append(self.pytqt_dir+"/tqt.so")
            # PyTDE libs
            linklist.append(self.pytde_dir+"/tdecore.so")

#            linklist.append("-L"+self.sip_dir); linklist.append("-lsip_tqt")
#            # PyTQt libs
#            linklist.append("-L"+self.pytqt_dir); linklist.append("-lqtcmodule")
#            # PyTDE libs
#            linklist.append("-L"+self.pytde_dir); linklist.append("-ltdecorecmodule"); linklist.append("-ltdeuicmodule")

            linklist.append("-L"+self.kde_lib_dir); linklist.append("-L/opt/trinity/lib"); linklist.append("-ltdecore"); linklist.append("-lpythonize")
            linklist.append("-L"+self.tqt_lib_dir); linklist.append("-lqt-mt")
            linklist.append("-lm")
            linklist.append("-lc")
            linklist.append(self.clib)

            linklist.append("-R"); linklist.append(self.python_dir)
            linklist.append("-R"); linklist.append(self.tqt_lib_dir)
            linklist.append("-R"); linklist.append(self.sip_dir)
            linklist.append("-R"); linklist.append(self.pytqt_dir)
            linklist.append("-R"); linklist.append(self.pytde_dir)
            linklist.append("-R"); linklist.append(self.kde_lib_dir)

            cmdlist.extend(linklist)
            spawn(cmdlist) # Execute!!
            print()

    cpptemplate = r"""
/*
 * Launch Control Centre modules written in Python using an embedded Python
 * interpreter.
 * Based on David Boddie's PyTDE-components.
 */

#include <stdio.h>
#include <Python.h>
#include <tdeinstance.h>
#define MODULE_DIR "%(moduledir)s"
#define MODULE_NAME "%(modulename)s"
#define FACTORY "SlaveFactory"

const char modname[] = MODULE_NAME;
#define MAIN_METHOD "dispatchLoop"

FILE *d = NULL;

PyObject* call_function(PyObject *callable, PyObject *args) {
    PyObject *result, *pArgs;

    if (callable == NULL) {
        printf(MODULE_NAME " tdeioslave error: callable == NULL in call_function\n");
        return NULL;
    }

    if (PyCallable_Check(callable)) {
        if(args == NULL) {
            pArgs = PyTuple_New(0);
        } else {
            pArgs = args;
        }
        result = PyObject_CallObject(callable, pArgs);

        /* If the arguments were created is this function then decrease
           their reference count. */
        if(args == NULL) {
            Py_XDECREF(pArgs);
            /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */
        }

        if(result == NULL) {
            PyErr_Print();
            PyErr_Clear();
        }
    }

    return result;
}

extern "C" {
    int kdemain( int argc, char **argv) {
        PyObject *pModule, *pName, *pDict;
        TDEInstance slave(MODULE_NAME);

        Py_SetProgramName(argv[0]);
        Py_Initialize();
        //PyEval_InitThreads();
        PySys_SetArgv(1, argv);

        PyRun_SimpleString("import sys\n");
        PyRun_SimpleString("sys.path.append('"MODULE_DIR"')\n");

        pName = PyBytes_FromString(modname);
        pModule = PyImport_Import(pName);

        Py_XDECREF(pName);

        if(pModule == NULL) {
            printf(MODULE_NAME " tdeioslave error: pModule == NULL\n");
            return 1;
        } else {
            PyObject *pClass, *pMethod, *pArgs, *pArg1, *pArg2, *pInstance;
            int i;

            pDict = PyModule_GetDict(pModule);
            /* pDict is a borrowed reference */

            pClass = PyDict_GetItemString(pDict, FACTORY);

            if(pClass == NULL) {
                printf(MODULE_NAME " tdeioslave error: pClass == NULL\n");
                return 1;
            } else {
                pArgs = PyTuple_New(2);

                pArg1 = PyBytes_FromString(argv[2]);
                pArg2 = PyBytes_FromString(argv[3]);

                PyTuple_SetItem(pArgs, 0, pArg1);
                PyTuple_SetItem(pArgs, 1, pArg2);

                call_function(pClass, pArgs);

                /* Some time later... */
                Py_XDECREF(pClass);
                Py_XDECREF(pArgs);
            }

            Py_XDECREF(pModule);
        }

        Py_Finalize();
        return 0;
    }
}
"""
###########################################################################
class InstallTdeioslave(Command):
    description = "Install Tdeioslave files"

    user_options = [
        ('install-dir=', 'd', "base directory for installing tdeioslave module files"),
        ('install-cmd=', None, "Command to use to install the files"),
        ('install-protocol-dir=',None,"directory for tdeioslave protocol files"),
        ('build-dir=','b', "build directory (where to install from)"),
        ('root=', None, "install everything relative to this alternate root directory"),
        ('force', 'f', "force installation (overwrite existing files)"),
        ('skip-build', None, "skip the build steps"),
        ]
    boolean_options = ['force', 'skip-build']

    def initialize_options(self):
        self.build_dir = None
        self.install_dir = None
        self.install_cmd = None
        self.install_protocol_dir = None
        self.outfiles = []
        self.root = None
        self.force = 0
        self.warn_dir = 1
        self.skip_build = None

    def finalize_options(self):
        own_install_dir = self.install_dir is not None
        own_install_protocol_dir = self.install_protocol_dir is not None

        self.set_undefined_options('install',
                                    ('build_base', 'build_dir'),
                                    ('install_tdeioslave', 'install_dir'),
                                    ('root', 'root'),
                                    ('force', 'force'),
                                    ('skip_build', 'skip_build'),
                                    ('install_cmd', 'install_cmd'),
                                    ('install_protocol','install_protocol_dir')
                                    )

        if own_install_dir and self.root is not None:
            self.install_dir = change_root(self.root,self.installdir)

        if own_install_protocol_dir and self.root is not None:
            self.install_protocol_dir = change_root(self.root,self.install_protocol_dir)

    def get_command_name(self):
        return 'install_tdeioslave'

    def run(self):
        if not self.skip_build:
            self.run_command('build_tdeioslave')

        self.announce("Installing Tdeioslave files...")

        for moduletuple in self.distribution.tdeioslaves:
            self.announce("Building Tdeioslave module from protocol file %s." % moduletuple[0])

            protocolfilename = moduletuple[0]

            # Read the protocol file
            libraryname = None
            try:
                fhandle = open(protocolfilename,'r')
                for line in fhandle.readlines():
                    parts = line.strip().split('=')
                    try:
                        if parts[0]=="exec":
                            libraryname = parts[1]
                    except IndexError:
                        pass
                fhandle.close()
            except IOError:
                raise SystemExit("Failed to find tdeioslave protocol file: %s" % moduletuple[0])

            if libraryname is None:
                raise SystemExit("Failed to find library name (Was there a exec entry in the protocol file?)")

            self.outfiles.extend(self.mkpath(self.install_protocol_dir))
            protocolfile_dest = os.path.join(self.install_protocol_dir,os.path.basename(protocolfilename))
            self.copy_file(protocolfilename, protocolfile_dest)

            stub_la_name = libraryname+'.la'

            self.outfiles.extend(self.mkpath(self.install_dir))

            # Install the library.
            cmdlist = ['libtool']
            cmdlist.append("--mode=install")
            cmdlist.append(self.install_cmd)
            cmdlist.append("-c")
            cmdlist.append(os.path.join(self.build_dir,stub_la_name))
            cmdlist.append(os.path.join(self.install_dir,stub_la_name))
            spawn(cmdlist) # Execute!!
            print()

            self.outfiles = [os.path.join(self.install_dir,os.path.basename(file)) for file in glob.glob(os.path.join(self.build_dir,'.libs',libraryname+'*'))]
            self.outfiles.append(protocolfile_dest)

        self.announce("Done installing Tdeioslave files.")

    def get_outputs(self):
        return self.outfiles or []

    def mkpath(self, name, mode=0o777):
        dir_util.mkpath(name, mode, dry_run=self.dry_run)
        return name

###########################################################################
class CheckPyTQt(Command):
    description = "Checks for the presence of a working PyTQt installation"

    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        self.min_tqt_version = self.distribution.min_tqt_version

    def run(self):
        if self.min_tqt_version!=None:
            tqtver,kdever = get_tqt_kde_versions()
            if compare_versions(self.min_tqt_version,tqtver)==1:
                raise SystemExit("Your TQt version is too old. Version %s or higher is required, found %s." % (self.min_tqt_version,tqtver))
            self.announce("Found TQt version %s." % tqtver)
        try:
            self.announce("Checking for a working PyTQt...")
            from PyTQt import tqt
            self.announce("  ...PyTQt is working")
        except:
            raise SystemExit("Couldn't import TQt! Please make sure that PyTQt is installed and working.")

    def get_outputs(self): return []

###########################################################################
class CheckPyTDE(Command):
    description = "Checks for the presence of a working PyTDE installation"

    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        self.min_kde_version = self.distribution.min_kde_version

    def run(self):
        if self.min_kde_version!=None:
            tqtver,kdever = get_tqt_kde_versions()
            if compare_versions(self.min_kde_version,kdever)==1:
                raise SystemExit("Your TDE version is too old. Version %s or higher is required, found %s." % (self.min_kde_version,kdever))
            self.announce("Found KDE version %s." % kdever)
        self.announce("Checking for a working PyTDE...")

        # Try to import modules one by one.
        for k_module in ('dcop', 'tdecore', 'tdeui', 'tdeio', 'tdefile', 'tdeparts', 'tdehtml', 'tdespell'):
            self.announce(k_module)
            try:
                exec('import ' + k_module)
            except:
                raise SystemExit("Error: Couldn't find module '" + k_module + "'. \n" + \
                    "Couldn't import KDE! Please make sure that PyTDE is installed and working.\n" + \
                    "PyTDE is available here: http://www.trinitydesktop.org")
        self.announce("  ...PyTDE is working")

    def get_outputs(self): return []

###########################################################################
def FindFileInPaths2(globpattern,canidatepaths):
    if canidatepaths is None or len(canidatepaths)==0:
        return (None,None)

    # Look for the globpattern on the path.
    for path in canidatepaths:
        if path!=None:
            files = glob.glob(os.path.join(path, globpattern))
            if len(files)!=0:
                return (path,os.path.basename(files[0]))

    # Continue searching with a breadth first search.
    dirs = []
    for path in canidatepaths:
        # Examine any directories on this path.
        dirlist = glob.glob(os.path.join(path, "*"))
        for item in dirlist:
            if os.path.isdir(item):
                # Examine each subdirectory.
                dirs.append(item)
    # Examine all subdirectories.
    return FindFileInPaths2(globpattern, dirs)

###########################################################################
def FindFileInPaths(globpattern,canidatepaths):
    x,y = FindFileInPaths2(globpattern,canidatepaths)
    return x

###########################################################################
# FIXME replace this with spawn.find_executable().
def FindExeOnPath(exe_name,high_prio_dirs=None,low_prio_dirs=None):
    candiate_paths = []

    if high_prio_dirs is not None:
        candiate_paths.extend(high_prio_dirs)

    path_var = os.getenv("PATH")
    candiate_paths.extend(path_var.split(':'))

    if low_prio_dirs is not None:
        candiate_paths.extend(low_prio_dirs)

    for dir in candiate_paths:
        if dir is not None:
            candiate = os.path.join(dir,exe_name)
            if os.path.isfile(candiate):
                if os.access(candiate,os.X_OK):
                    return candiate
    return None

###########################################################################

def ask_kde_config(question):
    # Look for the tde-config program
    kdeconfig = find_executable("tde-config", os.environ['PATH'] + os.pathsep + \
        os.pathsep.join(['/bin','/usr/bin','/opt/trinity/bin','/opt/kde/bin','/usr/local/bin']))
    if kdeconfig!=None:
        # Ask the tde-config program for the
        fhandle = os.popen(kdeconfig+' ' + question,'r')
        result = fhandle.read()
        fhandle.close()
        return result
    else:
        return None

###########################################################################
# Convert for example, "3.1.1a" => [3,1,1,'a']
#
def split_version_name(name):
    result = []
    accu = ''
    type = 0
    for c in name:
        if type==0:
            if c.isdigit():
                type = 1
            else:
                type = 2
            accu += c
        elif c=='.':
            if type==1:
                result.append(int(accu))
            else:
                result.append(accu)
            accu = ''
            type = 0
        elif type==1:
            # Digits
            if c.isdigit():
                accu += c
            else:
                result.append(int(accu))
                type = 2
                accu = c
        else:
            if c.isdigit():
                result.append(accu)
                type = 1
                accu = c
            else:
                accu += c
    if accu!='':
        result.append(accu)
    return result

###########################################################################
#
# Returns:
# -1 if a < b
# 0 if a and b are equal
# 1 if a > b
def compare_versions(a,b):
    aparts = split_version_name(a)
    bparts = split_version_name(b)
    if len(aparts)>len(bparts):
        compmax = len(aparts)
    else:
        compmax = len(bparts)
    i = 0
    for i in range(compmax):
        abit = 0
        if i<len(aparts):
            abit = aparts[i]
        bit = 0
        if i<len(bparts):
            bbit = bparts[i]
        if isinstance(abit,str) and not isinstance(bbit,str):
            return 1
        elif not isinstance(abit,str) and isinstance(bbit,str):
            return -1
        else:
            if abit>bbit:
                return 1
            elif abit<bbit:
                return -1
    return 0

###########################################################################
def get_tqt_kde_versions():
    versioninfo = ask_kde_config('--version')
    tqtver = None
    kdever = None
    if versioninfo!=None:
        for line in versioninfo.splitlines():
            if line.startswith("TQt: "):
                tqtver = line[4:]
            elif line.startswith("Qt: "):
                tqtver = line[4:]
            elif line.startswith("TDE: "):
                kdever = line[5:]
    return tqtver,kdever

###########################################################################
def compile_tqtdesigner(ui_files,
                  force=0,
                  prefix=None, base_dir=None,
                  verbose=1, dry_run=0):
    """Compile a collection of TQT Designer UI files to .py

    If 'dry_run' is true, doesn't actually do anything that would
    affect the filesystem.
    """
    generated_files = []
    for file in ui_files:
        if not file.endswith(".ui"):
            continue

        # Terminology from the py_compile module:
        #   cfile - byte-compiled file
        #   dfile - purported source filename (same as 'file' by default)
        if base_dir:
            file = os.path.join(base_dir ,file)

        pyfile = file[:-3] + '.py'
        uifile = file

        pyfile_base = os.path.basename(pyfile)
        if force or newer(uifile, pyfile):
            log.info("compiling TQt-Designer UI %s to %s", file, pyfile_base)
            if not dry_run:
                tqtuicompiler.CompileUI(uifile, pyfile)
            generated_files.append(pyfile)
        else:
            log.debug("skipping TQt-Designer compilation of %s to %s",
                      file, pyfile_base)
    return generated_files

###########################################################################
def get_libdir_name():
    if os.uname()[4] in ['x86_64','mips64','ppc64','sparc64','s390x','aarch64','ppc64le']:
        return 'lib64'
    else:
        return 'lib'
