1# -------------------------------------------------------------------- 2 3import re 4import os 5import sys 6import glob 7import copy 8import warnings 9 10try: 11 from cStringIO import StringIO 12except ImportError: 13 from io import StringIO 14 15try: 16 import setuptools 17except ImportError: 18 setuptools = None 19 20if setuptools: 21 from setuptools import setup as _setup 22 from setuptools import Extension as _Extension 23 from setuptools import Command 24else: 25 from distutils.core import setup as _setup 26 from distutils.core import Extension as _Extension 27 from distutils.core import Command 28 29def import_command(cmd): 30 try: 31 from importlib import import_module 32 except ImportError: 33 def import_module(n): 34 return __import__(n, fromlist=[None]) 35 try: 36 if not setuptools: raise ImportError 37 mod = import_module('setuptools.command.' + cmd) 38 return getattr(mod, cmd) 39 except ImportError: 40 mod = import_module('distutils.command.' + cmd) 41 return getattr(mod, cmd) 42 43_config = import_command('config') 44_build = import_command('build') 45_build_ext = import_command('build_ext') 46_install = import_command('install') 47 48from distutils import log 49from distutils import sysconfig 50from distutils.util import execute 51from distutils.util import split_quoted 52from distutils.errors import DistutilsError 53 54try: 55 from setuptools import dep_util 56except ImportError: 57 from distutils import dep_util 58 59try: 60 from packaging.version import Version 61except ImportError: 62 try: 63 from setuptools.extern.packaging.version import Version 64 except ImportError: 65 from distutils.version import StrictVersion as Version 66 67# -------------------------------------------------------------------- 68 69# Cython 70 71CYTHON = '0.29.32' 72 73def cython_req(): 74 return CYTHON 75 76def cython_chk(VERSION, verbose=True): 77 # 78 def warn(message): 79 if not verbose: return 80 ruler, ws, nl = "*"*80, " " ,"\n" 81 pyexe = sys.executable 82 advise = "$ %s -m pip install --upgrade cython" % pyexe 83 def printer(*s): sys.stderr.write(" ".join(s)+"\n") 84 printer(ruler, nl) 85 printer(ws, message, nl) 86 printer(ws, ws, advise, nl) 87 printer(ruler) 88 # 89 try: 90 import Cython 91 except ImportError: 92 warn("You need Cython to generate C source files.") 93 return False 94 # 95 CYTHON_VERSION = Cython.__version__ 96 m = re.match(r"(\d+\.\d+(?:\.\d+)?).*", CYTHON_VERSION) 97 if not m: 98 warn("Cannot parse Cython version string {0!r}" 99 .format(CYTHON_VERSION)) 100 return False 101 REQUIRED = Version(VERSION) 102 PROVIDED = Version(m.groups()[0]) 103 if PROVIDED < REQUIRED: 104 warn("You need Cython >= {0} (you have version {1})" 105 .format(VERSION, CYTHON_VERSION)) 106 return False 107 # 108 if verbose: 109 log.info("using Cython %s" % CYTHON_VERSION) 110 return True 111 112def cython_run( 113 source, target=None, 114 depends=(), includes=(), 115 workdir=None, force=False, 116 VERSION="0.0", 117): 118 if target is None: 119 target = os.path.splitext(source)[0]+'.c' 120 cwd = os.getcwd() 121 try: 122 if workdir: 123 os.chdir(workdir) 124 alldeps = [source] 125 for dep in depends: 126 alldeps += glob.glob(dep) 127 if not (force or dep_util.newer_group(alldeps, target)): 128 log.debug("skipping '%s' -> '%s' (up-to-date)", 129 source, target) 130 return 131 finally: 132 os.chdir(cwd) 133 require = 'Cython >= %s' % VERSION 134 if setuptools and not cython_chk(VERSION, verbose=False): 135 if sys.modules.get('Cython'): 136 removed = getattr(sys.modules['Cython'], '__version__', '') 137 log.info("removing Cython %s from sys.modules" % removed) 138 pkgname = re.compile(r'cython(\.|$)', re.IGNORECASE) 139 for modname in list(sys.modules.keys()): 140 if pkgname.match(modname): 141 del sys.modules[modname] 142 try: 143 install_setup_requires = setuptools._install_setup_requires 144 with warnings.catch_warnings(): 145 if hasattr(setuptools, 'SetuptoolsDeprecationWarning'): 146 category = setuptools.SetuptoolsDeprecationWarning 147 warnings.simplefilter('ignore', category) 148 log.info("fetching build requirement '%s'" % require) 149 install_setup_requires(dict(setup_requires=[require])) 150 except Exception: 151 log.info("failed to fetch build requirement '%s'" % require) 152 if not cython_chk(VERSION): 153 raise DistutilsError("unsatisfied build requirement '%s'" % require) 154 # 155 log.info("cythonizing '%s' -> '%s'", source, target) 156 from cythonize import cythonize 157 args = [] 158 if workdir: 159 args += ['--working', workdir] 160 args += [source] 161 if target: 162 args += ['--output-file', target] 163 err = cythonize(args) 164 if err: 165 raise DistutilsError( 166 "Cython failure: '%s' -> '%s'" % (source, target) 167 ) 168 169 170# -------------------------------------------------------------------- 171 172def fix_config_vars(names, values): 173 values = list(values) 174 if 'CONDA_BUILD' in os.environ: 175 return values 176 if sys.platform == 'darwin': 177 if 'ARCHFLAGS' in os.environ: 178 ARCHFLAGS = os.environ['ARCHFLAGS'] 179 for i, flag in enumerate(list(values)): 180 flag, count = re.subn('-arch\s+\w+', ' ', str(flag)) 181 if count and ARCHFLAGS: 182 flag = flag + ' ' + ARCHFLAGS 183 values[i] = flag 184 if 'SDKROOT' in os.environ: 185 SDKROOT = os.environ['SDKROOT'] 186 for i, flag in enumerate(list(values)): 187 flag, count = re.subn('-isysroot [^ \t]*', ' ', str(flag)) 188 if count and SDKROOT: 189 flag = flag + ' ' + '-isysroot ' + SDKROOT 190 values[i] = flag 191 return values 192 193def get_config_vars(*names): 194 # Core Python configuration 195 values = sysconfig.get_config_vars(*names) 196 # Do any distutils flags fixup right now 197 values = fix_config_vars(names, values) 198 return values 199 200from distutils.unixccompiler import UnixCCompiler 201rpath_option_orig = UnixCCompiler.runtime_library_dir_option 202def rpath_option(compiler, dir): 203 option = rpath_option_orig(compiler, dir) 204 if sys.platform[:5] == 'linux': 205 if option.startswith('-R'): 206 option = option.replace('-R', '-Wl,-rpath,', 1) 207 elif option.startswith('-Wl,-R'): 208 option = option.replace('-Wl,-R', '-Wl,-rpath,', 1) 209 return option 210UnixCCompiler.runtime_library_dir_option = rpath_option 211 212# -------------------------------------------------------------------- 213 214class PetscConfig: 215 216 def __init__(self, petsc_dir, petsc_arch, dest_dir=None): 217 if dest_dir is None: 218 dest_dir = os.environ.get('DESTDIR') 219 self.configdict = { } 220 if not petsc_dir: 221 raise DistutilsError("PETSc not found") 222 if not os.path.isdir(petsc_dir): 223 raise DistutilsError("invalid PETSC_DIR: %s" % petsc_dir) 224 self.version = self._get_petsc_version(petsc_dir) 225 self.configdict = self._get_petsc_config(petsc_dir, petsc_arch) 226 self.PETSC_DIR = self['PETSC_DIR'] 227 self.PETSC_ARCH = self['PETSC_ARCH'] 228 self.DESTDIR = dest_dir 229 language_map = {'CONLY':'c', 'CXXONLY':'c++'} 230 self.language = language_map[self['PETSC_LANGUAGE']] 231 232 def __getitem__(self, item): 233 return self.configdict[item] 234 235 def get(self, item, default=None): 236 return self.configdict.get(item, default) 237 238 def configure(self, extension, compiler=None): 239 self.configure_extension(extension) 240 if compiler is not None: 241 self.configure_compiler(compiler) 242 243 def _get_petsc_version(self, petsc_dir): 244 import re 245 version_re = { 246 'major' : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"), 247 'minor' : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"), 248 'micro' : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"), 249 'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+(-*\d+)"), 250 } 251 petscversion_h = os.path.join(petsc_dir, 'include', 'petscversion.h') 252 with open(petscversion_h, 'rt') as f: data = f.read() 253 major = int(version_re['major'].search(data).groups()[0]) 254 minor = int(version_re['minor'].search(data).groups()[0]) 255 micro = int(version_re['micro'].search(data).groups()[0]) 256 release = int(version_re['release'].search(data).groups()[0]) 257 return (major, minor, micro), (release == 1) 258 259 def _get_petsc_config(self, petsc_dir, petsc_arch): 260 from os.path import join, isdir, exists 261 PETSC_DIR = petsc_dir 262 PETSC_ARCH = petsc_arch 263 # 264 confdir = join('lib', 'petsc', 'conf') 265 if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))): 266 petscvars = join(PETSC_DIR, confdir, 'petscvariables') 267 PETSC_ARCH = makefile(open(petscvars, 'rt')).get('PETSC_ARCH') 268 if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))): 269 PETSC_ARCH = '' 270 # 271 variables = join(PETSC_DIR, confdir, 'variables') 272 if not exists(variables): 273 variables = join(PETSC_DIR, PETSC_ARCH, confdir, 'variables') 274 petscvariables = join(PETSC_DIR, PETSC_ARCH, confdir, 'petscvariables') 275 # 276 with open(variables) as f: 277 contents = f.read() 278 with open(petscvariables) as f: 279 contents += f.read() 280 # 281 confstr = 'PETSC_DIR = %s\n' % PETSC_DIR 282 confstr += 'PETSC_ARCH = %s\n' % PETSC_ARCH 283 confstr += contents 284 confdict = makefile(StringIO(confstr)) 285 return confdict 286 287 def _configure_ext(self, ext, dct, append=False): 288 extdict = ext.__dict__ 289 for key, values in dct.items(): 290 if key in extdict: 291 for value in values: 292 if value not in extdict[key]: 293 if not append: 294 extdict[key].insert(0, value) 295 else: 296 extdict[key].append(value) 297 298 def configure_extension(self, extension): 299 # includes and libraries 300 # paths in PETSc config files point to final installation location, but 301 # we might be building against PETSc in staging location (DESTDIR) when 302 # DESTDIR is set, so append DESTDIR (if nonempty) to those paths 303 petsc_inc = flaglist(prepend_to_flags(self.DESTDIR, self['PETSC_CC_INCLUDES'])) 304 lib_flags = prepend_to_flags(self.DESTDIR, '-L%s %s' % \ 305 (self['PETSC_LIB_DIR'], self['PETSC_LIB_BASIC'])) 306 petsc_lib = flaglist(lib_flags) 307 # runtime_library_dirs is not supported on Windows 308 if sys.platform != 'win32': 309 # if DESTDIR is set, then we're building against PETSc in a staging 310 # directory, but rpath needs to point to final install directory. 311 rpath = strip_prefix(self.DESTDIR, self['PETSC_LIB_DIR']) 312 petsc_lib['runtime_library_dirs'].append(rpath) 313 314 # Link in extra libraries on static builds 315 if self['BUILDSHAREDLIB'] != 'yes': 316 petsc_ext_lib = split_quoted(self['PETSC_EXTERNAL_LIB_BASIC']) 317 petsc_lib['extra_link_args'].extend(petsc_ext_lib) 318 self._configure_ext(extension, petsc_inc, append=True) 319 self._configure_ext(extension, petsc_lib) 320 321 def configure_compiler(self, compiler): 322 if compiler.compiler_type != 'unix': return 323 getenv = os.environ.get 324 # distutils C/C++ compiler 325 (cc, cflags, ccshared, cxx) = get_config_vars( 326 'CC', 'CFLAGS', 'CCSHARED', 'CXX') 327 ccshared = getenv('CCSHARED', ccshared or '') 328 cflags = getenv('CFLAGS', cflags or '') 329 cflags = cflags.replace('-Wstrict-prototypes', '') 330 # distutils linker 331 (ldflags, ldshared, so_ext) = get_config_vars( 332 'LDFLAGS', 'LDSHARED', 'SO') 333 ld = cc 334 ldshared = getenv('LDSHARED', ldshared) 335 ldflags = getenv('LDFLAGS', cflags + ' ' + (ldflags or '')) 336 ldcmd = split_quoted(ld) + split_quoted(ldflags) 337 ldshared = [flg for flg in split_quoted(ldshared) if flg not in ldcmd and (flg.find('/lib/spack/env')<0)] 338 ldshared = str.join(' ', ldshared) 339 # 340 def get_flags(cmd): 341 if not cmd: return '' 342 cmd = split_quoted(cmd) 343 if os.path.basename(cmd[0]) == 'xcrun': 344 del cmd[0] 345 while True: 346 if cmd[0] == '-sdk': 347 del cmd[0:2] 348 continue 349 if cmd[0] == '-log': 350 del cmd[0] 351 continue 352 break 353 return ' '.join(cmd[1:]) 354 # PETSc C compiler 355 PCC = self['PCC'] 356 PCC_FLAGS = get_flags(cc) + ' ' + self['PCC_FLAGS'] 357 PCC_FLAGS = PCC_FLAGS.replace('-fvisibility=hidden', '') 358 PCC = getenv('PCC', PCC) + ' ' + getenv('PCCFLAGS', PCC_FLAGS) 359 PCC_SHARED = str.join(' ', (PCC, ccshared, cflags)) 360 # PETSc C++ compiler 361 PCXX = PCC if self.language == 'c++' else self.get('CXX', cxx) 362 # PETSc linker 363 PLD = self['PCC_LINKER'] 364 PLD_FLAGS = get_flags(ld) + ' ' + self['PCC_LINKER_FLAGS'] 365 PLD_FLAGS = PLD_FLAGS.replace('-fvisibility=hidden', '') 366 PLD = getenv('PLD', PLD) + ' ' + getenv('PLDFLAGS', PLD_FLAGS) 367 PLD_SHARED = str.join(' ', (PLD, ldshared, ldflags)) 368 # 369 compiler.set_executables( 370 compiler = PCC, 371 compiler_cxx = PCXX, 372 linker_exe = PLD, 373 compiler_so = PCC_SHARED, 374 linker_so = PLD_SHARED, 375 ) 376 compiler.shared_lib_extension = so_ext 377 # 378 if sys.platform == 'darwin': 379 for attr in ('preprocessor', 380 'compiler', 'compiler_cxx', 'compiler_so', 381 'linker_so', 'linker_exe'): 382 compiler_cmd = getattr(compiler, attr, []) 383 while '-mno-fused-madd' in compiler_cmd: 384 compiler_cmd.remove('-mno-fused-madd') 385 386 def log_info(self): 387 PETSC_DIR = self['PETSC_DIR'] 388 PETSC_ARCH = self['PETSC_ARCH'] 389 version = ".".join([str(i) for i in self.version[0]]) 390 release = ("development", "release")[self.version[1]] 391 version_info = version + ' ' + release 392 integer_size = '%s-bit' % self['PETSC_INDEX_SIZE'] 393 scalar_type = self['PETSC_SCALAR'] 394 precision = self['PETSC_PRECISION'] 395 language = self['PETSC_LANGUAGE'] 396 compiler = self['PCC'] 397 linker = self['PCC_LINKER'] 398 log.info('PETSC_DIR: %s' % PETSC_DIR ) 399 log.info('PETSC_ARCH: %s' % PETSC_ARCH ) 400 log.info('version: %s' % version_info) 401 log.info('integer-size: %s' % integer_size) 402 log.info('scalar-type: %s' % scalar_type) 403 log.info('precision: %s' % precision) 404 log.info('language: %s' % language) 405 log.info('compiler: %s' % compiler) 406 log.info('linker: %s' % linker) 407 408# -------------------------------------------------------------------- 409 410class Extension(_Extension): 411 pass 412 413# -------------------------------------------------------------------- 414 415cmd_petsc_opts = [ 416 ('petsc-dir=', None, 417 "define PETSC_DIR, overriding environmental variables"), 418 ('petsc-arch=', None, 419 "define PETSC_ARCH, overriding environmental variables"), 420 ] 421 422 423class config(_config): 424 425 Configure = PetscConfig 426 427 user_options = _config.user_options + cmd_petsc_opts 428 429 def initialize_options(self): 430 _config.initialize_options(self) 431 self.petsc_dir = None 432 self.petsc_arch = None 433 434 def get_config_arch(self, arch): 435 return config.Configure(self.petsc_dir, arch) 436 437 def run(self): 438 _config.run(self) 439 self.petsc_dir = config.get_petsc_dir(self.petsc_dir) 440 if self.petsc_dir is None: return 441 petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch) 442 log.info('-' * 70) 443 log.info('PETSC_DIR: %s' % self.petsc_dir) 444 arch_list = petsc_arch 445 if not arch_list : 446 arch_list = [ None ] 447 for arch in arch_list: 448 conf = self.get_config_arch(arch) 449 archname = conf.PETSC_ARCH or conf['PETSC_ARCH'] 450 scalar_type = conf['PETSC_SCALAR'] 451 precision = conf['PETSC_PRECISION'] 452 language = conf['PETSC_LANGUAGE'] 453 compiler = conf['PCC'] 454 linker = conf['PCC_LINKER'] 455 log.info('-'*70) 456 log.info('PETSC_ARCH: %s' % archname) 457 log.info(' * scalar-type: %s' % scalar_type) 458 log.info(' * precision: %s' % precision) 459 log.info(' * language: %s' % language) 460 log.info(' * compiler: %s' % compiler) 461 log.info(' * linker: %s' % linker) 462 log.info('-' * 70) 463 464 #@staticmethod 465 def get_petsc_dir(petsc_dir): 466 if not petsc_dir: return None 467 petsc_dir = os.path.expandvars(petsc_dir) 468 if not petsc_dir or '$PETSC_DIR' in petsc_dir: 469 try: 470 import petsc 471 petsc_dir = petsc.get_petsc_dir() 472 except ImportError: 473 log.warn("PETSC_DIR not specified") 474 return None 475 petsc_dir = os.path.expanduser(petsc_dir) 476 petsc_dir = os.path.abspath(petsc_dir) 477 return config.chk_petsc_dir(petsc_dir) 478 get_petsc_dir = staticmethod(get_petsc_dir) 479 480 #@staticmethod 481 def chk_petsc_dir(petsc_dir): 482 if not os.path.isdir(petsc_dir): 483 log.error('invalid PETSC_DIR: %s (ignored)' % petsc_dir) 484 return None 485 return petsc_dir 486 chk_petsc_dir = staticmethod(chk_petsc_dir) 487 488 #@staticmethod 489 def get_petsc_arch(petsc_dir, petsc_arch): 490 if not petsc_dir: return None 491 petsc_arch = os.path.expandvars(petsc_arch) 492 if (not petsc_arch or '$PETSC_ARCH' in petsc_arch): 493 petsc_arch = '' 494 petsc_conf = os.path.join(petsc_dir, 'lib', 'petsc', 'conf') 495 if os.path.isdir(petsc_conf): 496 petscvariables = os.path.join(petsc_conf, 'petscvariables') 497 if os.path.exists(petscvariables): 498 conf = makefile(open(petscvariables, 'rt')) 499 petsc_arch = conf.get('PETSC_ARCH', '') 500 petsc_arch = petsc_arch.split(os.pathsep) 501 petsc_arch = unique(petsc_arch) 502 petsc_arch = [arch for arch in petsc_arch if arch] 503 return config.chk_petsc_arch(petsc_dir, petsc_arch) 504 get_petsc_arch = staticmethod(get_petsc_arch) 505 506 #@staticmethod 507 def chk_petsc_arch(petsc_dir, petsc_arch): 508 valid_archs = [] 509 for arch in petsc_arch: 510 arch_path = os.path.join(petsc_dir, arch) 511 if os.path.isdir(arch_path): 512 valid_archs.append(arch) 513 else: 514 log.warn("invalid PETSC_ARCH: %s (ignored)" % arch) 515 return valid_archs 516 chk_petsc_arch = staticmethod(chk_petsc_arch) 517 518 519class build(_build): 520 521 user_options = _build.user_options 522 user_options += [( 523 'inplace', 524 'i', 525 "ignore build-lib and put compiled extensions into the source " 526 "directory alongside your pure Python modules", 527 )] 528 user_options += cmd_petsc_opts 529 530 boolean_options = _build.boolean_options 531 boolean_options += ['inplace'] 532 533 def initialize_options(self): 534 _build.initialize_options(self) 535 self.inplace = None 536 self.petsc_dir = None 537 self.petsc_arch = None 538 539 def finalize_options(self): 540 _build.finalize_options(self) 541 if self.inplace is None: 542 self.inplace = False 543 self.set_undefined_options('config', 544 ('petsc_dir', 'petsc_dir'), 545 ('petsc_arch', 'petsc_arch')) 546 self.petsc_dir = config.get_petsc_dir(self.petsc_dir) 547 self.petsc_arch = config.get_petsc_arch(self.petsc_dir, 548 self.petsc_arch) 549 550 sub_commands = \ 551 [('build_src', lambda *args: True)] + \ 552 _build.sub_commands 553 554 555class build_src(Command): 556 description = "build C sources from Cython files" 557 558 user_options = [ 559 ('force', 'f', 560 "forcibly build everything (ignore file timestamps)"), 561 ] 562 563 boolean_options = ['force'] 564 565 def initialize_options(self): 566 self.force = False 567 568 def finalize_options(self): 569 self.set_undefined_options('build', 570 ('force', 'force'), 571 ) 572 573 def run(self): 574 sources = getattr(self, 'sources', []) 575 for source in sources: 576 cython_run( 577 force=self.force, 578 VERSION=cython_req(), 579 **source 580 ) 581 582 583class build_ext(_build_ext): 584 585 user_options = _build_ext.user_options + cmd_petsc_opts 586 587 def initialize_options(self): 588 _build_ext.initialize_options(self) 589 self.inplace = None 590 self.petsc_dir = None 591 self.petsc_arch = None 592 self._outputs = [] 593 594 def finalize_options(self): 595 _build_ext.finalize_options(self) 596 self.set_undefined_options('build', ('inplace', 'inplace')) 597 self.set_undefined_options('build', 598 ('petsc_dir', 'petsc_dir'), 599 ('petsc_arch', 'petsc_arch')) 600 if ((sys.platform.startswith('linux') or 601 sys.platform.startswith('gnu') or 602 sys.platform.startswith('sunos')) and 603 sysconfig.get_config_var('Py_ENABLE_SHARED')): 604 py_version = sysconfig.get_python_version() 605 bad_pylib_dir = os.path.join(sys.prefix, "lib", 606 "python" + py_version, 607 "config") 608 try: 609 self.library_dirs.remove(bad_pylib_dir) 610 except ValueError: 611 pass 612 pylib_dir = sysconfig.get_config_var("LIBDIR") 613 if pylib_dir not in self.library_dirs: 614 self.library_dirs.append(pylib_dir) 615 if pylib_dir not in self.rpath: 616 self.rpath.append(pylib_dir) 617 if sys.exec_prefix == '/usr': 618 self.library_dirs.remove(pylib_dir) 619 self.rpath.remove(pylib_dir) 620 621 def _copy_ext(self, ext): 622 extclass = ext.__class__ 623 fullname = self.get_ext_fullname(ext.name) 624 modpath = str.split(fullname, '.') 625 pkgpath = os.path.join('', *modpath[0:-1]) 626 name = modpath[-1] 627 sources = list(ext.sources) 628 newext = extclass(name, sources) 629 newext.__dict__.update(copy.deepcopy(ext.__dict__)) 630 newext.name = name 631 return pkgpath, newext 632 633 def _build_ext_arch(self, ext, pkgpath, arch): 634 build_temp = self.build_temp 635 build_lib = self.build_lib 636 try: 637 self.build_temp = os.path.join(build_temp, arch) 638 self.build_lib = os.path.join(build_lib, pkgpath, arch) 639 _build_ext.build_extension(self, ext) 640 finally: 641 self.build_temp = build_temp 642 self.build_lib = build_lib 643 644 def get_config_arch(self, arch): 645 return config.Configure(self.petsc_dir, arch) 646 647 def build_extension(self, ext): 648 if not isinstance(ext, Extension): 649 return _build_ext.build_extension(self, ext) 650 petsc_arch = self.petsc_arch 651 if not petsc_arch: 652 petsc_arch = [ None ] 653 for arch in petsc_arch: 654 config = self.get_config_arch(arch) 655 ARCH = arch or config['PETSC_ARCH'] 656 if ARCH not in self.PETSC_ARCH_LIST: 657 self.PETSC_ARCH_LIST.append(ARCH) 658 self.DESTDIR = config.DESTDIR 659 ext.language = config.language 660 config.log_info() 661 pkgpath, newext = self._copy_ext(ext) 662 config.configure(newext, self.compiler) 663 self._build_ext_arch(newext, pkgpath, ARCH) 664 665 def run(self): 666 self.build_sources() 667 _build_ext.run(self) 668 669 def build_sources(self): 670 if 'build_src' in self.distribution.cmdclass: 671 self.run_command('build_src') 672 673 def build_extensions(self, *args, **kargs): 674 self.PETSC_ARCH_LIST = [] 675 _build_ext.build_extensions(self, *args,**kargs) 676 if not self.PETSC_ARCH_LIST: return 677 self.build_configuration(self.PETSC_ARCH_LIST) 678 679 def build_configuration(self, arch_list): 680 # 681 template, variables = self.get_config_data(arch_list) 682 config_data = template % variables 683 # 684 build_lib = self.build_lib 685 dist_name = self.distribution.get_name() 686 config_file = os.path.join(build_lib, dist_name, 'lib', 687 dist_name.replace('4py', '') + '.cfg') 688 # 689 def write_file(filename, data): 690 with open(filename, 'w') as fh: 691 fh.write(config_data) 692 execute(write_file, (config_file, config_data), 693 msg='writing %s' % config_file, 694 verbose=self.verbose, dry_run=self.dry_run) 695 696 def get_config_data(self, arch_list): 697 DESTDIR = self.DESTDIR 698 template = "\n".join([ 699 "PETSC_DIR = %(PETSC_DIR)s", 700 "PETSC_ARCH = %(PETSC_ARCH)s", 701 ]) + "\n" 702 variables = { 703 'PETSC_DIR' : strip_prefix(DESTDIR, self.petsc_dir), 704 'PETSC_ARCH' : os.path.pathsep.join(arch_list), 705 } 706 return template, variables 707 708 def copy_extensions_to_source(self): 709 build_py = self.get_finalized_command('build_py') 710 for ext in self.extensions: 711 inp_file, reg_file = self._get_inplace_equivalent(build_py, ext) 712 713 arch_list = [''] 714 if isinstance(ext, Extension) and self.petsc_arch: 715 arch_list = self.petsc_arch[:] 716 717 file_pairs = [] 718 inp_head, inp_tail = os.path.split(inp_file) 719 reg_head, reg_tail = os.path.split(reg_file) 720 for arch in arch_list: 721 inp_file = os.path.join(inp_head, arch, inp_tail) 722 reg_file = os.path.join(reg_head, arch, reg_tail) 723 file_pairs.append((inp_file, reg_file)) 724 725 for inp_file, reg_file in file_pairs: 726 if os.path.exists(reg_file) or not ext.optional: 727 dest_dir, _ = os.path.split(inp_file) 728 self.mkpath(dest_dir) 729 self.copy_file(reg_file, inp_file, level=self.verbose) 730 731 def get_outputs(self): 732 self.check_extensions_list(self.extensions) 733 outputs = [] 734 for ext in self.extensions: 735 fullname = self.get_ext_fullname(ext.name) 736 filename = self.get_ext_filename(fullname) 737 if isinstance(ext, Extension) and self.petsc_arch: 738 head, tail = os.path.split(filename) 739 for arch in self.petsc_arch: 740 outfile = os.path.join(self.build_lib, head, arch, tail) 741 outputs.append(outfile) 742 else: 743 outfile = os.path.join(self.build_lib, filename) 744 outputs.append(outfile) 745 outputs = list(set(outputs)) 746 return outputs 747 748 749class install(_install): 750 751 def initialize_options(self): 752 with warnings.catch_warnings(): 753 if setuptools: 754 if hasattr(setuptools, 'SetuptoolsDeprecationWarning'): 755 category = setuptools.SetuptoolsDeprecationWarning 756 warnings.simplefilter('ignore', category) 757 _install.initialize_options(self) 758 self.old_and_unmanageable = True 759 760 761cmdclass_list = [ 762 config, 763 build, 764 build_src, 765 build_ext, 766 install, 767] 768 769# -------------------------------------------------------------------- 770 771def setup(**attrs): 772 cmdclass = attrs.setdefault('cmdclass', {}) 773 for cmd in cmdclass_list: 774 cmdclass.setdefault(cmd.__name__, cmd) 775 build_src.sources = attrs.pop('cython_sources', None) 776 use_setup_requires = False # handle Cython requirement ourselves 777 if setuptools and build_src.sources and use_setup_requires: 778 version = cython_req() 779 if not cython_chk(version, verbose=False): 780 reqs = attrs.setdefault('setup_requires', []) 781 reqs += ['Cython>='+version] 782 return _setup(**attrs) 783 784# -------------------------------------------------------------------- 785 786if setuptools: 787 try: 788 from setuptools.command import egg_info as mod_egg_info 789 _FileList = mod_egg_info.FileList 790 class FileList(_FileList): 791 def process_template_line(self, line): 792 level = log.set_threshold(log.ERROR) 793 try: 794 _FileList.process_template_line(self, line) 795 finally: 796 log.set_threshold(level) 797 mod_egg_info.FileList = FileList 798 except (ImportError, AttributeError): 799 pass 800 801# -------------------------------------------------------------------- 802 803def append(seq, item): 804 if item not in seq: 805 seq.append(item) 806 807def append_dict(conf, dct): 808 for key, values in dct.items(): 809 if key in conf: 810 for value in values: 811 if value not in conf[key]: 812 conf[key].append(value) 813def unique(seq): 814 res = [] 815 for item in seq: 816 if item not in res: 817 res.append(item) 818 return res 819 820def flaglist(flags): 821 822 conf = { 823 'define_macros' : [], 824 'undef_macros' : [], 825 'include_dirs' : [], 826 827 'libraries' : [], 828 'library_dirs' : [], 829 'runtime_library_dirs': [], 830 831 'extra_compile_args' : [], 832 'extra_link_args' : [], 833 } 834 835 if type(flags) is str: 836 flags = flags.split() 837 838 switch = '-Wl,' 839 newflags = [] 840 linkopts = [] 841 for f in flags: 842 if f.startswith(switch): 843 if len(f) > 4: 844 append(linkopts, f[4:]) 845 else: 846 append(newflags, f) 847 if linkopts: 848 newflags.append(switch + ','.join(linkopts)) 849 flags = newflags 850 851 append_next_word = None 852 853 for word in flags: 854 855 if append_next_word is not None: 856 append(append_next_word, word) 857 append_next_word = None 858 continue 859 860 switch, value = word[0:2], word[2:] 861 862 if switch == "-I": 863 append(conf['include_dirs'], value) 864 elif switch == "-D": 865 try: 866 idx = value.index("=") 867 macro = (value[:idx], value[idx+1:]) 868 except ValueError: 869 macro = (value, None) 870 append(conf['define_macros'], macro) 871 elif switch == "-U": 872 append(conf['undef_macros'], value) 873 elif switch == "-l": 874 append(conf['libraries'], value) 875 elif switch == "-L": 876 append(conf['library_dirs'], value) 877 elif switch == "-R": 878 append(conf['runtime_library_dirs'], value) 879 elif word.startswith("-Wl"): 880 linkopts = word.split(',') 881 append_dict(conf, flaglist(linkopts[1:])) 882 elif word == "-rpath": 883 append_next_word = conf['runtime_library_dirs'] 884 elif word == "-Xlinker": 885 append_next_word = conf['extra_link_args'] 886 else: 887 #log.warn("unrecognized flag '%s'" % word) 888 pass 889 return conf 890 891def prepend_to_flags(path, flags): 892 """Prepend a path to compiler flags with absolute paths""" 893 if not path: 894 return flags 895 def append_path(m): 896 switch = m.group(1) 897 open_quote = m.group(4) 898 old_path = m.group(5) 899 close_quote = m.group(6) 900 if os.path.isabs(old_path): 901 moded_path = os.path.normpath(path + os.path.sep + old_path) 902 return switch + open_quote + moded_path + close_quote 903 return m.group(0) 904 return re.sub(r'((^|\s+)(-I|-L))(\s*["\']?)(\S+)(["\']?)', 905 append_path, flags) 906 907def strip_prefix(prefix, string): 908 if not prefix: 909 return string 910 return re.sub(r'^' + prefix, '', string) 911 912# -------------------------------------------------------------------- 913 914from distutils.text_file import TextFile 915 916# Regexes needed for parsing Makefile-like syntaxes 917import re as _re 918_variable_rx = _re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") 919_findvar1_rx = _re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") 920_findvar2_rx = _re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") 921 922def makefile(fileobj, dct=None): 923 """Parse a Makefile-style file. 924 925 A dictionary containing name/value pairs is returned. If an 926 optional dictionary is passed in as the second argument, it is 927 used instead of a new dictionary. 928 """ 929 fp = TextFile(file=fileobj, 930 strip_comments=1, 931 skip_blanks=1, 932 join_lines=1) 933 934 if dct is None: 935 dct = {} 936 done = {} 937 notdone = {} 938 939 while 1: 940 line = fp.readline() 941 if line is None: # eof 942 break 943 m = _variable_rx.match(line) 944 if m: 945 n, v = m.group(1, 2) 946 v = str.strip(v) 947 if "$" in v: 948 notdone[n] = v 949 else: 950 try: v = int(v) 951 except ValueError: pass 952 done[n] = v 953 try: del notdone[n] 954 except KeyError: pass 955 fp.close() 956 957 # do variable interpolation here 958 while notdone: 959 for name in list(notdone.keys()): 960 value = notdone[name] 961 m = _findvar1_rx.search(value) or _findvar2_rx.search(value) 962 if m: 963 n = m.group(1) 964 found = True 965 if n in done: 966 item = str(done[n]) 967 elif n in notdone: 968 # get it on a subsequent round 969 found = False 970 else: 971 done[n] = item = "" 972 if found: 973 after = value[m.end():] 974 value = value[:m.start()] + item + after 975 if "$" in after: 976 notdone[name] = value 977 else: 978 try: value = int(value) 979 except ValueError: 980 done[name] = str.strip(value) 981 else: 982 done[name] = value 983 del notdone[name] 984 else: 985 # bogus variable reference; 986 # just drop it since we can't deal 987 del notdone[name] 988 # save the results in the global dictionary 989 dct.update(done) 990 return dct 991 992# -------------------------------------------------------------------- 993