view setup.py @ 5:33684971cdb1

Replaced the shell script launcher with a cross-platform C executable. * Replaced the Unix-only shell script launcher with a cross-platform compiled executable written in C. * Added several new configuration variables to the SConsctruct script which allows the user to specify where their Python site-packages and executable are located; * Cleaned up the SConstruct script and made it easier to grok.
author M. George Hansen <technopolitica@gmail.com>
date Tue, 17 May 2011 14:18:25 -0700
parents 1fd2201f5c36
children
line wrap: on
line source

#!/usr/bin/env python2
"""
Cross-system setup script responsible for creating source and binary
packages for Linux, Windows, and Mac
"""
import sys
import os
import string
from distutils.core import setup, Command, Distribution as _Distribution
from distutils.command import (install as distutils_install,
    build as distutils_build)
from distutils.util import subst_vars, convert_path

distutils_install.SCHEME_KEYS += ('config',)

# Update existing installation schemes with the new config key.
distutils_install.INSTALL_SCHEMES['unix_prefix'].update(
    {
        'data'  : '$base/share/$dist_name',
        'config': '$base/etc/$dist_name'
    }
)
distutils_install.INSTALL_SCHEMES['unix_local'].update(
    {
        'data'  : '$base/local/share/$dist_name',
        'config': '$base/local/etc/$dist_name'
    }
)
distutils_install.INSTALL_SCHEMES['deb_system'].update(
    {
        'data'  : '$base/share/$dist_name',
        'config': '$base/etc/$dist_name'
    }
)
distutils_install.INSTALL_SCHEMES['unix_home'].update(
    {
        'data'  : '$base/share/$dist_name',
        'config': '$base/etc/$dist_name'
    }
)
distutils_install.INSTALL_SCHEMES['unix_user'].update(
    {
        'data'  : '$userbase/share/$dist_name',
        'config': '$userbase/etc/$dist_name'
    }
)
distutils_install.WINDOWS_SCHEME.update(
    {
        'config': '$base'
    }
)
distutils_install.INSTALL_SCHEMES['nt_user'].update(
    {
        'config': '$userbase',
    }
)
distutils_install.INSTALL_SCHEMES['mac'].update(
    {
        'config': '$base',
    }
)
distutils_install.INSTALL_SCHEMES['mac_user'].update(
    {
        'config': '$userbase',
    }
)
distutils_install.INSTALL_SCHEMES['os2'].update(
    {
        'config': '$base',
    }
)
distutils_install.INSTALL_SCHEMES['os2_home'].update(
    {
        'config': '$userbase',
    }
)


class build_templates(Command):
    description = '"build" template files (copy to build directory, ' \
                  'substituting build variables)'
    user_options = [
        ('build-dir=', 'd', 'directory to "build" (copy) to'),
        ('force', 'f', 'forcibly build everything (ignore file timestamps)'),
    ]
    boolean_options = ['force']
    
    def initialize_options(self):
        self.build_base = None
        self.build_dir = None
        self.force = None
        self.config_vars = None
        self.templates = None
    
    def finalize_options(self):
        self.set_undefined_options(
            'build',
            ('build_base', 'build_base'),
            ('force', 'force'),
        )
        self.build_dir = os.path.join(self.build_base, 'templates')
        self.set_undefined_options('install', ('config_vars', 'config_vars'))
        self.templates = self.distribution.templates
    
    def run(self):
        if self.has_templates():
            self.build_templates()
    
    def build_templates(self):
        build_dir = self.build_dir
        for template_path, outfile_rel_path in self.templates.items():
            outfile_path = os.path.join(build_dir, outfile_rel_path)
            self.copy_file(template_path, outfile_path, preserve_mode=False)
            self.substitute_template(outfile_path)
    
    def substitute_template(self, file_path):
        with file(file_path, 'r') as template_file:
            template_content = template_file.read()
        template = string.Template(template_content)
        config_vars = self.config_vars
        file_content = template.substitute(config_vars)
        with file(file_path, 'w') as config_file:
            config_file.write(file_content)
    
    def has_templates(self):
        return self.distribution.has_templates()


class install_config(Command):
    description = 'install configuration files'
    user_options = [
        ('install-dir=', 'd', 'directory to install configuration files to'),
        ('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.force = 0
        self.skip_build = None
        self.config_files = None
        self.config_templates = None
        self.config_vars = None
    
    def finalize_options(self):
        self.set_undefined_options(
            'install',
            ('install_config', 'install_dir'),
            ('force', 'force'),
            ('skip_build', 'skip_build'),
            ('config_vars', 'config_vars'),
        )
        self.config_files = self.distribution.config_files
        self.config_templates = self.distribution.config_templates
    
    def run(self):
        install_dir = self.install_dir
        outfiles = []
        for file_path in self.config_files:
            output_file_path = os.path.join(install_dir, file_path)
            output_dir_path = os.path.dirname(output_file_path)
            self.mkpath(output_dir_path)
            outfile = self.copy_file(file_path, output_dir_path,
                                     preserve_mode=0)
            outfiles.append(outfile)
        self.outfiles = outfiles
    
    def get_inputs(self):
        return self.distribution.config_files or []
    
    def get_outputs(self):
        return self.outfiles or []


class install(distutils_install.install):
    user_options = distutils_install.install.user_options + [
        ('install-config=', None,
         'installation directory for system-wide configuration files')
    ]
    
    def has_config(self):
        return self.distribution.has_config()
    
    def has_templates(self):
        return self.distribution.has_templates()

    def initialize_options(self):
        self.install_config = None
        distutils_install.install.initialize_options(self)
    
    def finalize_options(self):
        distutils_install.install.finalize_options(self)
        # FIXME M. George Hansen 2011-05-11: Probably shouldn't be using a
        #     private method here...
        self._expand_attrs(['install_config'])
        self.convert_paths('config')
        self.config_vars['config'] = self.install_config
        if self.root is not None:
            self.change_roots('config')
        install_scheme = self.install_scheme
        for key, value in install_scheme.items():
            if os.name in ['posix', 'nt']:
                value = os.path.expanduser(value)
            value = subst_vars(value, self.config_vars)
            value = convert_path(value)
            self.config_vars[key] = value
    
    def select_scheme(self, name):
        # Store the selected scheme for later processing.
        distutils_install.install.select_scheme(self, name)
        self.install_scheme = distutils_install.INSTALL_SCHEMES[name]

    def get_program_files_path(self):
        assert sys.platform == 'win32'
        try:
            program_files_path = os.environ['ProgramFiles']
        except KeyError:
            # ProgramFiles environmental variable isn't set, so we'll have to
            # find it ourselves. Bit of a hack, but better than nothing.
            program_files_path = ''
            try:
                import win32api
            except ImportError:                
                  program_files_path = ''
            else:
                drive_names = \
                    [drive_name for drive_name in
                     win32api.GetLogicalDriveStrings().split('\000') if
                     drive_name]
                for drive_name in drive_names:
                    search_path = os.path.join(drive_name, 'Program Files')
                    if os.path.isdir(search_path):
                        program_files_path = search_path
            if not program_files_path:
                error_message = 'unable to determine path to Program Files'
                raise error_message
        
        return program_files_path
    
    sub_commands = distutils_install.install.sub_commands + [
        ('install_config', has_config),
    ]


class build(distutils_build.build):
    def has_templates(self):
        return self.distribution.has_templates()
    
    sub_commands = [('build_templates', has_templates)] + \
        distutils_build.build.sub_commands


class Distribution(_Distribution):
    def __init__(self, attrs=None):
        self.templates = {}
        self.config_files = []
        _Distribution.__init__(self, attrs)
    
    def has_config(self):
        return self.config_files and len(self.config_files) > 0

    def has_templates(self):
        return self.templates and len(self.templates) > 0


setup(
    name='parpg',
    scripts=['parpg'],
    templates={'system.cfg': 'system.cfg.in', 'parpg': 'parpg.in'},
    config_files=['system.cfg'],
    packages=['parpg'],
    modules=['src/main.py'],
    package_dir={'parpg': 'src/parpg'},
    version='0.2.0',
    url='http://www.parpg.net',
    description='',
    long_description='',
    cmdclass={'install': install, 'install_config': install_config,
              'build': build, 'build_templates': build_templates},
    distclass=Distribution,
    classifiers=[
        'Programming Language :: Python',
        'License :: OSI Approved :: GNU General Public License (GPL)',
        'Intended Audience :: End Users/Desktop',
        'Intended Audience :: Developers',
        'Development Status :: 2 - Pre-Alpha',
        'Operating System :: OS Independent',
        'Natural Language :: English',
        'Topic :: Games/Entertainment :: Role-Playing',
    ],
)