Home | History | Annotate | Download | only in py3
      1 # -*- coding: ascii -*-
      2 #
      3 # Copyright 2007, 2008, 2009, 2010, 2011
      4 # Andr\xe9 Malo or his licensors, as applicable
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 """
     18 ===================
     19  Command extenders
     20 ===================
     21 
     22 Command extenders.
     23 """
     24 __author__ = "Andr\xe9 Malo"
     25 __docformat__ = "restructuredtext en"
     26 __test__ = False
     27 
     28 from distutils import fancy_getopt as _fancy_getopt
     29 from distutils import log
     30 from distutils.command import build as _build
     31 from distutils.command import build_ext as _build_ext
     32 from distutils.command import install as _install
     33 from distutils.command import install_data as _install_data
     34 from distutils.command import install_lib as _install_lib
     35 import os as _os
     36 
     37 _option_defaults = {}
     38 _option_inherits = {}
     39 _option_finalizers = {}
     40 _command_mapping = {
     41     'install': 'Install',
     42     'install_data': 'InstallData',
     43     'install_lib': 'InstallLib',
     44     'build': 'Build',
     45     'build_ext': 'BuildExt',
     46 }
     47 
     48 
     49 def add_option(command, long_name, help_text, short_name=None, default=None,
     50                inherit=None):
     51     """ Add an option """
     52     try:
     53         command_class = globals()[_command_mapping[command]]
     54     except KeyError:
     55         raise ValueError("Unknown command %r" % (command,))
     56     for opt in command_class.user_options:
     57         if opt[0] == long_name:
     58             break
     59     else:
     60         opt = (long_name, short_name, help_text)
     61         command_class.user_options.append(opt)
     62         if not long_name.endswith('='):
     63             command_class.boolean_options.append(long_name)
     64             attr_name = _fancy_getopt.translate_longopt(long_name)
     65         else:
     66             attr_name = _fancy_getopt.translate_longopt(long_name[:-1])
     67         if command not in _option_defaults:
     68             _option_defaults[command] = []
     69         if inherit is not None:
     70             if isinstance(inherit, str):
     71                 inherit = [inherit]
     72             for i_inherit in inherit:
     73                 add_option(
     74                     i_inherit, long_name, help_text, short_name, default
     75                 )
     76             default = None
     77             if command not in _option_inherits:
     78                 _option_inherits[command] = []
     79             for i_inherit in inherit:
     80                 for i_command, opt_name in _option_inherits[command]:
     81                     if i_command == i_inherit and opt_name == attr_name:
     82                         break
     83                 else:
     84                     _option_inherits[command].append((i_inherit, attr_name))
     85         _option_defaults[command].append((attr_name, default))
     86 
     87 
     88 def add_finalizer(command, key, func):
     89     """ Add finalizer """
     90     if command not in _option_finalizers:
     91         _option_finalizers[command] = {}
     92     if key not in _option_finalizers[command]:
     93         _option_finalizers[command][key] = func
     94 
     95 
     96 class Install(_install.install):
     97     """ Extended installer to reflect the additional data options """
     98     user_options = _install.install.user_options + [
     99         ('single-version-externally-managed', None,
    100             "Compat option. Does not a thing."),
    101     ]
    102     boolean_options = _install.install.boolean_options + [
    103         'single-version-externally-managed'
    104     ]
    105 
    106     def initialize_options(self):
    107         """ Prepare for new options """
    108         _install.install.initialize_options(self)
    109         self.single_version_externally_managed = None
    110         if 'install' in _option_defaults:
    111             for opt_name, default in _option_defaults['install']:
    112                 setattr(self, opt_name, default)
    113 
    114     def finalize_options(self):
    115         """ Finalize options """
    116         _install.install.finalize_options(self)
    117         if 'install' in _option_inherits:
    118             for parent, opt_name in _option_inherits['install']:
    119                 self.set_undefined_options(parent, (opt_name, opt_name))
    120         if 'install' in _option_finalizers:
    121             for func in list(_option_finalizers['install'].values()):
    122                 func(self)
    123 
    124 
    125 class InstallData(_install_data.install_data):
    126     """ Extended data installer """
    127     user_options = _install_data.install_data.user_options + []
    128     boolean_options = _install_data.install_data.boolean_options + []
    129 
    130     def initialize_options(self):
    131         """ Prepare for new options """
    132         _install_data.install_data.initialize_options(self)
    133         if 'install_data' in _option_defaults:
    134             for opt_name, default in _option_defaults['install_data']:
    135                 setattr(self, opt_name, default)
    136 
    137     def finalize_options(self):
    138         """ Finalize options """
    139         _install_data.install_data.finalize_options(self)
    140         if 'install_data' in _option_inherits:
    141             for parent, opt_name in _option_inherits['install_data']:
    142                 self.set_undefined_options(parent, (opt_name, opt_name))
    143         if 'install_data' in _option_finalizers:
    144             for func in list(_option_finalizers['install_data'].values()):
    145                 func(self)
    146 
    147 
    148 class InstallLib(_install_lib.install_lib):
    149     """ Extended lib installer """
    150     user_options = _install_lib.install_lib.user_options + []
    151     boolean_options = _install_lib.install_lib.boolean_options + []
    152 
    153     def initialize_options(self):
    154         """ Prepare for new options """
    155         _install_lib.install_lib.initialize_options(self)
    156         if 'install_lib' in _option_defaults:
    157             for opt_name, default in _option_defaults['install_lib']:
    158                 setattr(self, opt_name, default)
    159 
    160     def finalize_options(self):
    161         """ Finalize options """
    162         _install_lib.install_lib.finalize_options(self)
    163         if 'install_lib' in _option_inherits:
    164             for parent, opt_name in _option_inherits['install_lib']:
    165                 self.set_undefined_options(parent, (opt_name, opt_name))
    166         if 'install_lib' in _option_finalizers:
    167             for func in list(_option_finalizers['install_lib'].values()):
    168                 func(self)
    169 
    170 
    171 class BuildExt(_build_ext.build_ext):
    172     """
    173     Extended extension builder class
    174 
    175     This class allows extensions to provide a ``check_prerequisites`` method
    176     which is called before actually building it. The method takes the
    177     `BuildExt` instance and returns whether the extension should be skipped or
    178     not.
    179     """
    180 
    181     def initialize_options(self):
    182         """ Prepare for new options """
    183         _build_ext.build_ext.initialize_options(self)
    184         if 'build_ext' in _option_defaults:
    185             for opt_name, default in _option_defaults['build_ext']:
    186                 setattr(self, opt_name, default)
    187 
    188     def finalize_options(self):
    189         """ Finalize options """
    190         _build_ext.build_ext.finalize_options(self)
    191         if 'build_ext' in _option_inherits:
    192             for parent, opt_name in _option_inherits['build_ext']:
    193                 self.set_undefined_options(parent, (opt_name, opt_name))
    194         if 'build_ext' in _option_finalizers:
    195             for func in list(_option_finalizers['build_ext'].values()):
    196                 func(self)
    197 
    198     def build_extension(self, ext):
    199         """
    200         Build C extension - with extended functionality
    201 
    202         The following features are added here:
    203 
    204         - ``ext.check_prerequisites`` is called before the extension is being
    205           built. See `Extension` for details. If the method does not exist,
    206           simply no check will be run.
    207         - The macros ``EXT_PACKAGE`` and ``EXT_MODULE`` will be filled (or
    208           unset) depending on the extensions name, but only if they are not
    209           already defined.
    210 
    211         :Parameters:
    212           `ext` : `Extension`
    213             The extension to build. If it's a pure
    214             ``distutils.core.Extension``, simply no prequisites check is
    215             applied.
    216 
    217         :Return: whatever ``distutils.command.build_ext.build_ext`` returns
    218         :Rtype: any
    219         """
    220         # handle name macros
    221         macros = dict(ext.define_macros or ())
    222         tup = ext.name.split('.')
    223         if len(tup) == 1:
    224             pkg, mod = None, tup[0]
    225         else:
    226             pkg, mod = '.'.join(tup[:-1]), tup[-1]
    227         if pkg is not None and 'EXT_PACKAGE' not in macros:
    228             ext.define_macros.append(('EXT_PACKAGE', pkg))
    229         if 'EXT_MODULE' not in macros:
    230             ext.define_macros.append(('EXT_MODULE', mod))
    231         if pkg is None:
    232             macros = dict(ext.undef_macros or ())
    233             if 'EXT_PACKAGE' not in macros:
    234                 ext.undef_macros.append('EXT_PACKAGE')
    235 
    236         # handle prereq checks
    237         try:
    238             checker = ext.check_prerequisites
    239         except AttributeError:
    240             pass
    241         else:
    242             if checker(self):
    243                 log.info("Skipping %s extension" % ext.name)
    244                 return
    245 
    246         return _build_ext.build_ext.build_extension(self, ext)
    247 
    248 
    249 class Build(_build.build):
    250 
    251     def initialize_options(self):
    252         """ Prepare for new options """
    253         _build.build.initialize_options(self)
    254         if 'build' in _option_defaults:
    255             for opt_name, default in _option_defaults['build']:
    256                 setattr(self, opt_name, default)
    257 
    258     def finalize_options(self):
    259         """ Finalize options """
    260         _build.build.finalize_options(self)
    261         if 'build' in _option_inherits:
    262             for parent, opt_name in _option_inherits['build']:
    263                 self.set_undefined_options(parent, (opt_name, opt_name))
    264         if 'build' in _option_finalizers:
    265             for func in list(_option_finalizers['build'].values()):
    266                 func(self)
    267