xref: /petsc/config/BuildSystem/config/packages/petsc4py.py (revision 58256f306a3b839fcaddd686dc61d2820e658499)
1import config.package
2import os
3
4class Configure(config.package.Package):
5  def __init__(self, framework):
6    config.package.Package.__init__(self, framework)
7    self.functions              = []
8    self.includes               = []
9    self.useddirectly           = 0
10    self.linkedbypetsc          = 0
11    self.builtafterpetsc        = 1
12    self.PrefixWriteCheck       = 0
13    return
14
15  def setupHelp(self,help):
16    import nargs
17    help.addArgument('PETSc', '-with-petsc4py=<bool>', nargs.ArgBool(None, False, 'Build PETSc Python bindings (petsc4py)'))
18    help.addArgument('PETSc', '-with-petsc4py-test-np=<np>',nargs.ArgInt(None, None, min=1, help='Number of processes to use for petsc4py tests'))
19    help.addArgument('PETSc', '-with-numpy-include=<dir>', nargs.Arg(None, None, 'Path to numpy headers from numpy.get_include() (default: autodetect)'))
20    return
21
22  def __str__(self):
23    if self.found:
24      s = 'petsc4py:\n'
25      if hasattr(self,'pythonpath'):
26        s += '  PYTHONPATH: '+self.pythonpath+'\n'
27      return s
28    return ''
29
30  def setupDependencies(self, framework):
31    config.package.Package.setupDependencies(self, framework)
32    self.python          = framework.require('config.packages.Python',self)
33    self.setCompilers    = framework.require('config.setCompilers',self)
34    self.sharedLibraries = framework.require('PETSc.options.sharedLibraries', self)
35    self.installdir      = framework.require('PETSc.options.installDir',self)
36    self.mpi             = framework.require('config.packages.MPI',self)
37    self.cython          = framework.require('config.packages.Cython',self)
38    self.slepc           = framework.require('config.packages.SLEPc',self)  # build SLEPc first
39    self.bamg            = framework.require('config.packages.BAMG',self)   # build BAMG first
40    self.odeps           = [self.cython,self.slepc,self.bamg]
41    return
42
43  def getDir(self):
44    return os.path.join('src','binding','petsc4py')
45
46  def Install(self):
47    import os
48    installLibPath = os.path.join(self.installDir, 'lib')
49    if self.setCompilers.isDarwin(self.log):
50      apple = 'You may need to\n (csh/tcsh) setenv MACOSX_DEPLOYMENT_TARGET 10.X\n (sh/bash) MACOSX_DEPLOYMENT_TARGET=10.X; export MACOSX_DEPLOYMENT_TARGET\nbefore running make on PETSc'
51    else:
52      apple = ''
53    self.logClearRemoveDirectory()
54    self.logResetRemoveDirectory()
55    archflags = ""
56    if self.setCompilers.isDarwin(self.log):
57      if self.setCompilers.isARM(self.log):
58        archflags = "ARCHFLAGS=\'-arch arm64\' "
59      elif self.types.sizes['void-p'] == 4:
60        archflags = "ARCHFLAGS=\'-arch i386\' "
61      else:
62        archflags = "ARCHFLAGS=\'-arch x86_64\' "
63
64    # Set PETSC_DIR/PETSC_ARCH to point at the dir with the PETSc installation:
65    # if DESTDIR is non-empty, then PETSc has been installed into staging dir
66    # if prefix has been specified at config time, path to PETSc includes that prefix
67    if self.argDB['prefix'] and not 'package-prefix-hash' in self.argDB:
68      newdir = 'PETSC_DIR=${DESTDIR}' + os.path.abspath(os.path.expanduser(self.argDB['prefix'])) + ' PETSC_ARCH= '
69    else:
70      newdir = ''
71
72    # Pass to setup.py if given, otherwise setup.py will autodetect
73    numpy_include = self.argDB.get('with-numpy-include')
74    if numpy_include is not None:
75      newdir += 'NUMPY_INCLUDE="'+numpy_include+'" '
76
77    self.addDefine('PETSC4PY_INSTALL_PATH','"'+os.path.join(self.installdir.dir,'lib')+'"')
78    cflags = ''
79    # by default, multiple flags are added by setup.py (-DNDEBUG -O3 -g), no matter the type of PETSc build
80    # this is problematic with Intel compilers, which take extremely long to compile bindings when using -g
81    # so we instead force no additional flags (other than the ones already used by PETSc, i.e., CFLAGS)
82    # TODO FIXME: this observation was made with Intel(R) oneAPI DPC++/C++ Compiler 2025.1.0 (2025.1.0.20250317), but it may be fixed in a subsequent release
83    if config.setCompilers.Configure.isIntel(self.getCompiler(), self.log):
84      cflags = 'CFLAGS=\'\' '
85    self.addPost(self.packageDir, ['${RM} -rf build',
86                                   newdir + archflags + cflags + ' PYTHONPATH=${PETSCPYTHONPATH} ' + self.python.pyexe + ' setup.py build',
87                                   'MPICC=${PCC} ' + newdir + archflags + self.python.pyexe +' setup.py install --install-lib=' + installLibPath + ' $(if $(DESTDIR),--root=\'$(DESTDIR)\')'])
88    self.pythonpath = installLibPath
89    np = self.make.make_test_np
90    if self.mpi.usingMPIUni:
91      np = 1
92    # TODO: some tests currently have issues with np > 4, this should be fixed
93    np = min(np,4)
94    if 'with-petsc4py-test-np' in self.argDB and self.argDB['with-petsc4py-test-np']:
95      np = self.argDB['with-petsc4py-test-np']
96    self.addMakeMacro('PETSC4PY_NP',np)
97    self.addTest('.', 'PYTHONPATH=%s:${PETSCPYTHONPATH} PETSC_OPTIONS="%s" ${MPIEXEC} -n ${PETSC4PY_NP} %s %s --verbose' % (installLibPath, '${PETSC_OPTIONS} -check_pointer_intensity 0 -error_output_stdout -malloc_dump ${PETSC_TEST_OPTIONS}', self.python.pyexe, os.path.join(self.packageDir, 'test', 'runtests.py')))
98    self.found = True
99    self.python.path.add(installLibPath)
100    return self.installDir
101
102  def configureLibrary(self):
103    import sys
104    if not self.sharedLibraries.useShared and not self.setCompilers.isCygwin(self.log):
105      raise RuntimeError('petsc4py requires PETSc be built with shared libraries; rerun with --with-shared-libraries')
106    if sys.version_info < (3, 6):
107      raise RuntimeError('petsc4py requires Python 3.6 at least')
108    chkpkgs = ['numpy']
109    if sys.version_info >= (3, 12):
110      chkpkgs.append('setuptools')
111    npkgs  = []
112    for pkg in chkpkgs:
113      if not getattr(self.python,pkg): npkgs.append(pkg)
114    if npkgs:
115      raise RuntimeError('petsc4py requires Python with "%s" module(s) installed!\n'
116                         'Please install using package managers - for ex: "apt" or "dnf" (on linux),\n'
117                         'or with "pip" using: %s -m pip install %s' % (" ".join(npkgs), self.python.pyexe, " ".join(npkgs)))
118    self.getInstallDir()
119
120  def alternateConfigureLibrary(self):
121    '''This is ugly but currently .gitlab-ci.yml is hardwired to use petsc4pytest'''
122    self.addMakeRule('petsc4pytest','')
123