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