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