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