1#!/usr/bin/env python 2# Copyright (C) 2003-2020 CAMP 3# Please see the accompanying LICENSE file for further information. 4 5import os 6import re 7import sys 8from pathlib import Path 9from subprocess import PIPE, run 10from sysconfig import get_platform 11 12from setuptools import Extension, find_packages, setup 13from setuptools.command.build_ext import build_ext as _build_ext 14from setuptools.command.develop import develop as _develop 15from setuptools.command.install import install as _install 16 17from config import build_interpreter, check_dependencies, write_configuration 18 19assert sys.version_info >= (3, 6) 20 21# Get the current version number: 22txt = Path('gpaw/__init__.py').read_text() 23version = re.search("__version__ = '(.*)'", txt)[1] 24ase_version_required = re.search("__ase_version_required__ = '(.*)'", txt)[1] 25 26description = 'GPAW: DFT and beyond within the projector-augmented wave method' 27long_description = Path('README.rst').read_text() 28 29remove_default_flags = False 30if '--remove-default-flags' in sys.argv: 31 remove_default_flags = True 32 sys.argv.remove('--remove-default-flags') 33 34for i, arg in enumerate(sys.argv): 35 if arg.startswith('--customize='): 36 custom = arg.split('=')[1] 37 raise DeprecationWarning( 38 f'Please set GPAW_CONFIG={custom} or place {custom} in ' + 39 '~/.gpaw/siteconfig.py') 40 41libraries = ['xc'] 42library_dirs = [] 43include_dirs = [] 44extra_link_args = [] 45extra_compile_args = ['-Wall', '-Wno-unknown-pragmas', '-std=c99'] 46runtime_library_dirs = [] 47extra_objects = [] 48define_macros = [('NPY_NO_DEPRECATED_API', '7'), 49 ('GPAW_NO_UNDERSCORE_CBLACS', '1'), 50 ('GPAW_NO_UNDERSCORE_CSCALAPACK', '1')] 51undef_macros = ['NDEBUG'] 52 53mpi_libraries = [] 54mpi_library_dirs = [] 55mpi_include_dirs = [] 56mpi_runtime_library_dirs = [] 57mpi_define_macros = [] 58 59parallel_python_interpreter = False 60compiler = None 61noblas = False 62nolibxc = False 63fftw = False 64scalapack = False 65libvdwxc = False 66elpa = False 67 68if os.name != 'nt' and run(['which', 'mpicc'], stdout=PIPE).returncode == 0: 69 mpicompiler = 'mpicc' 70else: 71 mpicompiler = None 72 73mpilinker = mpicompiler 74 75# Search and store current git hash if possible 76try: 77 from ase.utils import search_current_git_hash 78 githash = search_current_git_hash('gpaw') 79 if githash is not None: 80 define_macros += [('GPAW_GITHASH', githash)] 81 else: 82 print('.git directory not found. GPAW git hash not written.') 83except ImportError: 84 print('ASE not found. GPAW git hash not written.') 85 86# User provided customizations: 87gpaw_config = os.environ.get('GPAW_CONFIG') 88if gpaw_config and not Path(gpaw_config).is_file(): 89 raise FileNotFoundError(gpaw_config) 90for siteconfig in [gpaw_config, 91 'siteconfig.py', 92 '~/.gpaw/siteconfig.py']: 93 if siteconfig is not None: 94 path = Path(siteconfig).expanduser() 95 if path.is_file(): 96 print('Reading configuration from', path) 97 exec(path.read_text()) 98 break 99else: # no break 100 if not noblas: 101 libraries.append('blas') 102 103if not parallel_python_interpreter and mpicompiler: 104 # Build MPI-interface into _gpaw.so: 105 compiler = mpicompiler 106 define_macros += [('PARALLEL', '1')] 107 108platform_id = os.getenv('CPU_ARCH') 109if platform_id: 110 os.environ['_PYTHON_HOST_PLATFORM'] = get_platform() + '-' + platform_id 111 112if compiler is not None: 113 # A hack to change the used compiler and linker: 114 try: 115 # distutils is deprecated and will be removed in 3.12 116 from distutils.sysconfig import get_config_vars 117 except ImportError: 118 from sysconfig import get_config_vars 119 vars = get_config_vars() 120 if remove_default_flags: 121 for key in ['BASECFLAGS', 'CFLAGS', 'OPT', 'PY_CFLAGS', 122 'CCSHARED', 'CFLAGSFORSHARED', 'LINKFORSHARED', 123 'LIBS', 'SHLIBS']: 124 if key in vars: 125 value = vars[key].split() 126 # remove all gcc flags (causing problems with other compilers) 127 for v in list(value): 128 value.remove(v) 129 vars[key] = ' '.join(value) 130 for key in ['CC', 'LDSHARED']: 131 if key in vars: 132 value = vars[key].split() 133 # first argument is the compiler/linker. Replace with mpicompiler: 134 value[0] = compiler 135 vars[key] = ' '.join(value) 136 137for flag, name in [(noblas, 'GPAW_WITHOUT_BLAS'), 138 (nolibxc, 'GPAW_WITHOUT_LIBXC'), 139 (fftw, 'GPAW_WITH_FFTW'), 140 (scalapack, 'GPAW_WITH_SL'), 141 (libvdwxc, 'GPAW_WITH_LIBVDWXC'), 142 (elpa, 'GPAW_WITH_ELPA')]: 143 if flag: 144 define_macros.append((name, '1')) 145 146sources = [Path('c/bmgs/bmgs.c')] 147sources += Path('c').glob('*.c') 148sources += Path('c/xc').glob('*.c') 149if nolibxc: 150 for name in ['libxc.c', 'm06l.c', 151 'tpss.c', 'revtpss.c', 'revtpss_c_pbe.c', 152 'xc_mgga.c']: 153 sources.remove(Path(f'c/xc/{name}')) 154# Make build process deterministic (for "reproducible build") 155sources = [str(source) for source in sources] 156sources.sort() 157 158check_dependencies(sources) 159 160# Convert Path objects to str: 161library_dirs = [str(dir) for dir in library_dirs] 162include_dirs = [str(dir) for dir in include_dirs] 163 164extensions = [Extension('_gpaw', 165 sources, 166 libraries=libraries, 167 library_dirs=library_dirs, 168 include_dirs=include_dirs, 169 define_macros=define_macros, 170 undef_macros=undef_macros, 171 extra_link_args=extra_link_args, 172 extra_compile_args=extra_compile_args, 173 runtime_library_dirs=runtime_library_dirs, 174 extra_objects=extra_objects)] 175 176write_configuration(define_macros, include_dirs, libraries, library_dirs, 177 extra_link_args, extra_compile_args, 178 runtime_library_dirs, extra_objects, mpicompiler, 179 mpi_libraries, mpi_library_dirs, mpi_include_dirs, 180 mpi_runtime_library_dirs, mpi_define_macros) 181 182 183class build_ext(_build_ext): 184 def run(self): 185 import numpy as np 186 self.include_dirs.append(np.get_include()) 187 188 _build_ext.run(self) 189 190 if parallel_python_interpreter: 191 include_dirs.append(np.get_include()) 192 # Also build gpaw-python: 193 error = build_interpreter( 194 define_macros, include_dirs, libraries, 195 library_dirs, extra_link_args, extra_compile_args, 196 runtime_library_dirs, extra_objects, 197 mpicompiler, mpilinker, mpi_libraries, 198 mpi_library_dirs, 199 mpi_include_dirs, 200 mpi_runtime_library_dirs, mpi_define_macros) 201 assert error == 0 202 203 204def copy_gpaw_python(cmd, dir: str) -> None: 205 major, minor = sys.version_info[:2] 206 plat = get_platform() + f'-{major}.{minor}' 207 source = f'build/bin.{plat}/gpaw-python' 208 target = os.path.join(dir, 'gpaw-python') 209 cmd.copy_file(source, target) 210 211 212class install(_install): 213 def run(self): 214 _install.run(self) 215 copy_gpaw_python(self, self.install_scripts) 216 217 218class develop(_develop): 219 def run(self): 220 _develop.run(self) 221 copy_gpaw_python(self, self.script_dir) 222 223 224cmdclass = {'build_ext': build_ext} 225if parallel_python_interpreter: 226 cmdclass['install'] = install 227 cmdclass['develop'] = develop 228 229files = ['gpaw-analyse-basis', 'gpaw-basis', 230 'gpaw-plot-parallel-timings', 'gpaw-runscript', 231 'gpaw-setup', 'gpaw-upfplot'] 232scripts = [str(Path('tools') / script) for script in files] 233 234 235setup(name='gpaw', 236 version=version, 237 description=description, 238 long_description=long_description, 239 maintainer='GPAW-community', 240 maintainer_email='gpaw-users@listserv.fysik.dtu.dk', 241 url='https://wiki.fysik.dtu.dk/gpaw', 242 license='GPLv3+', 243 platforms=['unix'], 244 packages=find_packages(), 245 entry_points={'console_scripts': ['gpaw = gpaw.cli.main:main']}, 246 setup_requires=['numpy'], 247 install_requires=[f'ase>={ase_version_required}', 248 'scipy>=1.2.0'], 249 ext_modules=extensions, 250 scripts=scripts, 251 cmdclass=cmdclass, 252 classifiers=[ 253 'Development Status :: 6 - Mature', 254 'License :: OSI Approved :: ' 255 'GNU General Public License v3 or later (GPLv3+)', 256 'Operating System :: OS Independent', 257 'Programming Language :: Python :: 3', 258 'Programming Language :: Python :: 3.6', 259 'Programming Language :: Python :: 3.7', 260 'Programming Language :: Python :: 3.8', 261 'Programming Language :: Python :: 3.9', 262 'Topic :: Scientific/Engineering :: Physics']) 263