forked from zerothi/sisl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
596 lines (490 loc) · 18.1 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
#!/usr/bin/env python3
"""
sisl: Generic library for manipulating DFT output, geometries and tight-binding parameter sets
"""
# We have used a paradigm following pandas and Cython web-page documentation.
if __doc__ is None:
__doc__ = """sisl: Generic library for manipulating DFT output, geometries and tight-binding parameter sets"""
DOCLINES = __doc__.split("\n")
import sys
import subprocess
import multiprocessing
import os
import os.path as osp
import argparse
# pkg_resources are part of setuptools
import pkg_resources
# We should *always* import setuptools prior to Cython/distutils
import setuptools
def _ospath(path):
""" Changes '/' separators to OS separators """
return osp.join(*path.split('/'))
# Define a list of minimum versions
min_version ={
"python": "3.6",
"numpy": "1.13",
"pyparsing": "1.5.7",
"xarray": "0.10.0",
}
# Macros for use when compiling stuff
macros = []
try:
import Cython
# if available we can cythonize stuff
_CYTHON_VERSION = Cython.__version__
from Cython.Build import cythonize
# We currently do not have any restrictions on Cython (I think?)
# If so, simply put it here, and we will not use it
_CYTHON_INSTALLED = True
except ImportError:
_CYTHON_VERSION = None
_CYTHON_INSTALLED = False
cythonize = lambda x, *args, **kwargs: x # dummy func
if _CYTHON_INSTALLED:
# The import of Extension must be after the import of Cython, otherwise
# we do not get the appropriately patched class.
# See https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html
# Now we can import cython distutils
from Cython.Distutils.old_build_ext import old_build_ext as cython_build_ext
cython = True
else:
cython = False
# Allow users to remove cython step (forcefully)
# This may break compilation, but at least users should be aware
if "--no-cythonize" in sys.argv:
sys.argv.remove("--no-cythonize")
cython = False
# Check if users requests coverage of Cython sources
if "--with-cython-coverage" in sys.argv:
linetrace = True
sys.argv.remove("--with-cython-coverage")
else:
linetrace = False
# Define Cython directives
# We shouldn't rely on sources having the headers filled
# with directives.
# Cleaner to have them here, and possibly on a per file
# basis (if needed).
# That could easily be added at ext_cython place
directives = {"linetrace": False, "language_level": 3}
if linetrace:
# https://pypkg.com/pypi/pytest-cython/f/tests/example-project/setup.py
directives["linetrace"] = True
directives["emit_code_comments"] = True
macros.extend([("CYTHON_TRACE", "1"), ("CYTHON_TRACE_NOGIL", "1")])
# We will *only* use setuptools
# Although setuptools is not shipped with the standard library, I think
# this is ok since it should get installed pretty easily.
from setuptools import Command, Extension
from setuptools import find_packages
# Patch to allow fortran sources in setup
# build_ext requires numpy setup
# Also for extending build schemes we require build_ext from numpy.distutils
from distutils.command.sdist import sdist
from numpy.distutils.command.build_ext import build_ext as numpy_build_ext
from numpy.distutils.core import Extension as FortranExtension
from numpy.distutils.core import setup
if not cython:
cython_build_ext = numpy_build_ext
# Custom command classes
cmdclass = {}
# Now create the build extensions
class CythonCommand(cython_build_ext):
"""
Custom distutils command subclassed from Cython.Distutils.build_ext
to compile pyx->c, and stop there. All this does is override the
C-compile method build_extension() with a no-op.
"""
def build_extension(self, ext):
pass
if cython:
# we have cython and generate c codes directly
suffix = ".pyx"
cmdclass["cython"] = CythonCommand
else:
suffix = ".c"
# Retrieve the compiler information
from numpy.distutils.system_info import get_info
# use flags defined in numpy
all_info = get_info('ALL')
# Define compilation flags
extra_compile_args = ""
extra_link_args = extra_compile_args
# in numpy>=1.16.0, silence build warnings about deprecated API usage
macros.append(("NPY_NO_DEPRECATED_API", "0"))
# Do not expose multiple platform Cython code.
# We do not need it
# https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#integrating-multiple-modules
macros.append(("CYTHON_NO_PYINIT_EXPORT", "1"))
class EnsureSource_sdist(sdist):
"""Ensure Cython has runned on all pyx files (i.e. we need c sources)."""
def initialize_options(self):
super().initialize_options()
def run(self):
if "cython" in cmdclass:
self.run_command("cython")
else:
pyx_files = [(_pyxfiles, "c"), (self._cpp_pyxfiles, "cpp")]
for pyxfiles, extension in [(_pyxfiles, "c")]:
for pyxfile in pyxfiles:
sourcefile = pyxfile[:-3] + extension
msg = (f"{extension}-source file '{sourcefile}' not found.\n"
"Run 'setup.py cython' before sdist."
)
assert os.path.isfile(sourcefile), msg
super().run()
cmdclass["sdist"] = EnsureSource_sdist
_pyxfiles = [
"sisl/_indices.pyx",
"sisl/_math_small.pyx",
"sisl/physics/_bloch.pyx",
"sisl/physics/_matrix_ddk.pyx",
"sisl/physics/_matrix_dk.pyx",
"sisl/physics/_matrix_k.pyx",
"sisl/physics/_matrix_phase3.pyx",
"sisl/physics/_matrix_phase3_nc.pyx",
"sisl/physics/_matrix_phase3_so.pyx",
"sisl/physics/_matrix_phase_nc_diag.pyx",
"sisl/physics/_matrix_phase_nc.pyx",
"sisl/physics/_matrix_phase.pyx",
"sisl/physics/_matrix_phase_so.pyx",
"sisl/physics/_phase.pyx",
"sisl/_sparse.pyx",
"sisl/_supercell.pyx",
]
# Prepopulate the ext_cython to create extensions
# Later, when we complicate things more, we
# may need the dictionary to add include statements etc.
# I.e. ext_cython[...] = {pyxfile: ...,
# include: ...,
# depends: ...,
# sources: ...}
# All our extensions depend on numpy/core/include
numpy_incl = pkg_resources.resource_filename("numpy", _ospath("core/include"))
ext_cython = {}
for pyx in _pyxfiles:
# remove ".pyx"
pyx_src = pyx[:-4]
pyx_mod = pyx_src.replace("/", ".")
ext_cython[pyx_mod] = {
"pyxfile": _ospath(pyx_src),
"include": [numpy_incl]
}
#ext_cython[pyx_mod] = {"pyxfile": _ospath(pyx_src)}
ext_cython["sisl._sparse"]["depends"] = [_ospath("sisl/_indices.pxd")]
# List of extensions for setup(...)
extensions = []
for name, data in ext_cython.items():
sources = [data["pyxfile"] + suffix] + data.get("sources", [])
ext = Extension(name,
sources=sources,
depends=data.get("depends", []),
include_dirs=data.get("include", None),
language=data.get("language", "c"),
define_macros=macros + data.get("macros", []),
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
extensions.append(ext)
# Specific Fortran extensions
ext_fortran = {
"sisl.io.siesta._siesta": {
"sources": [_ospath(f"sisl/io/siesta/_src/{f}") for f in
["io_m.f90",
"siesta_sc_off.f90",
"hsx_read.f90", "hsx_write.f90",
"dm_read.f90", "dm_write.f90",
"tshs_read.f90", "tshs_write.f90",
"grid_read.f90", "grid_write.f90",
"gf_read.f90", "gf_write.f90",
"tsde_read.f90", "tsde_write.f90",
"hs_read.f90",
"wfsx_read.f90"]
],
},
}
for name, data in ext_fortran.items():
ext = FortranExtension(name,
sources=data.get("sources"),
depends=data.get("depends", []),
include_dirs=data.get("include", None),
define_macros=macros + data.get("macros", []),
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
extensions.append(ext)
class EnsureBuildExt(numpy_build_ext):
"""
Override build-ext to check whether compilable sources are present
This merely pretty-prints a better error message.
Note we require build_ext to inherit from numpy.distutils since
we need fortran sources.
"""
def check_cython_extensions(self, extensions):
for ext in extensions:
for src in ext.sources:
if not os.path.exists(src):
print(f"{ext.name}: -> {ext.sources}")
raise Exception(
f"""Cython-generated file '{src}' not found.
Cython is required to compile sisl from a development branch.
Please install Cython or download a release package of sisl.
""")
def build_extensions(self):
self.check_cython_extensions(self.extensions)
numpy_build_ext.build_extensions(self)
# Override build_ext command (typically called by setuptools)
cmdclass["build_ext"] = EnsureBuildExt
class InfoCommand(Command):
"""
Custom distutils command to create the sisl/info.py file.
It will additionally print out standard information abou
the version.
"""
description = "create info.py file"
user_options = []
boolean_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
print('info RUNNING')
cmdclass["info.py"] = InfoCommand
# Run cythonizer
def cythonizer(extensions, *args, **kwargs):
"""
Skip cythonizer (regardless) when running
* clean
* sdist
Otherwise if `cython` is True, we will cythonize sources.
"""
if "clean" in sys.argv or "sdist" in sys.argv:
# https://github.com/cython/cython/issues/1495
return extensions
elif not cython:
raise RuntimeError("Cannot cythonize without Cython installed.")
# Retrieve numpy include directories for headesr
numpy_incl = pkg_resources.resource_filename("numpy", _ospath("core/include"))
# Allow parallel flags to be used while cythonizing
parser = argparse.ArgumentParser()
parser.add_argument("-j", type=int, dest="parallel")
parser.add_argument("--parallel", type=int, dest="parallel")
parsed, _ = parser.parse_known_args()
if parsed.parallel:
kwargs["nthreads"] = max(0, parsed.parallel)
# Extract Cython extensions
# And also other extensions to store them
other_extensions = []
cython_extensions = []
for ext in extensions:
if ext.name in ext_cython:
cython_extensions.append(ext)
else:
other_extensions.append(ext)
return other_extensions + cythonize(cython_extensions, *args, quiet=False, **kwargs)
MAJOR = 0
MINOR = 10
MICRO = 0
ISRELEASED = True
VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO)
GIT_REVISION = "ed02b72d44a47c475a253874e51683ed6a6cc169"
REVISION_YEAR = 2020
DISTNAME = "sisl"
LICENSE = "LGPLv3"
AUTHOR = "sisl developers"
URL = "https://github.com/zerothi/sisl"
DOWNLOAD_URL = "https://github.com/zerothi/sisl/releases"
PROJECT_URLS = {
"Bug Tracker": "https://github.com/zerothi/sisl/issues",
"Documentation": "https://zerothi.github.io/sisl",
"Source Code": "https://github.com/zerothi/sisl",
}
CLASSIFIERS = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Cython",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Physics",
"Topic :: Utilities",
]
# The install_requires should also be the
# requirements for the actual running of sisl
setuptools_kwargs = {
"python_requires": ">= " + min_version["python"],
"install_requires": [
"setuptools",
"numpy >= " + min_version["numpy"],
"scipy",
"netCDF4",
"pyparsing >= " + min_version["pyparsing"],
],
"setup_requires": [
"numpy >= " + min_version["numpy"],
],
"extras_require": {
# We currently use xarray for additional data-analysis
# And tqdm for progressbars
"analysis": [
"xarray >= " + min_version["xarray"],
"tqdm",
],
},
"zip_safe": False,
}
def readme():
if not osp.exists("README.md"):
return ""
return open("README.md", "r").read()
metadata = dict(
name=DISTNAME,
author=AUTHOR,
maintainer=AUTHOR,
description="Python interface for tight-binding model creation and analysis of DFT output. Input mechanism for large scale transport calculations using NEGF TBtrans (TranSiesta)",
long_description=readme(),
long_description_content_type="text/markdown",
url="https://github.com/zerothi/sisl",
download_url=DOWNLOAD_URL,
license=LICENSE,
# Ensure the packages are being found in the correct locations
package_dir={"sisl_toolbox": "toolbox"},
packages=
# We need to add sisl.* since that recursively adds modules
find_packages(include=["sisl", "sisl.*"])
+
# Add toolboxes
# This requires some name-mangling since we can't place them
# in the correct place unless we use 'package_dir' and this trick.
# 1. Here we list files as they should appear in packages for end-users
# 2. In 'package_dir' we defer the package name to the local file path
list(map(lambda x: f"sisl_toolbox.{x}", find_packages("toolbox"))),
ext_modules=cythonizer(extensions, compiler_directives=directives),
entry_points={
"console_scripts":
["sgeom = sisl.geometry:sgeom",
"sgrid = sisl.grid:sgrid",
"sdata = sisl.utils._sisl_cmd:sisl_cmd",
"sisl = sisl.utils._sisl_cmd:sisl_cmd",
# Add toolbox CLI
"ts_poisson = sisl_toolbox.transiesta.poisson.poisson_explicit:poisson_explicit_cli",
],
},
classifiers=CLASSIFIERS,
platforms="any",
project_urls=PROJECT_URLS,
cmdclass=cmdclass,
**setuptools_kwargs
)
cwd = osp.abspath(osp.dirname(__file__))
if not osp.exists(_ospath(cwd + "/PKG-INFO")):
# Generate Cython sources, unless building from source release
# generate_cython()
pass
def git_version():
global GIT_REVISION
def _minimal_ext_cmd(cmd):
# construct minimal environment
env = {}
for k in ["SYSTEMROOT", "PATH"]:
v = os.environ.get(k)
if v is not None:
env[k] = v
# LANGUAGE is used on win32
env["LANGUAGE"] = "C"
env["LANG"] = "C"
env["LC_ALL"] = "C"
out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env).communicate()[0]
return out.strip().decode("ascii")
current_path = osp.dirname(osp.realpath(__file__))
try:
# Get top-level directory
git_dir = _minimal_ext_cmd(["git", "rev-parse", "--show-toplevel"])
# Assert that the git-directory is consistent with this setup.py script
if git_dir != current_path:
raise ValueError("Not executing the top-setup.py script")
# Get latest revision tag
rev = _minimal_ext_cmd(["git", "rev-parse", "HEAD"])
# Get latest tag
tag = _minimal_ext_cmd(["git", "describe", "--abbrev=0"])
# Get number of commits since tag
count = _minimal_ext_cmd(["git", "rev-list", tag + "..", "--count"])
if len(count) == 0:
count = "1"
# Get year
year = int(_minimal_ext_cmd(["git", "show", "-s", "--format=%ci"]).split("-")[0])
print("sisl-install: using git revision")
except Exception as e:
print("sisl-install: using internal shipped revisions")
# Retain the revision name
rev = GIT_REVISION
# Assume it is on tag
count = "0"
year = REVISION_YEAR
return rev, int(count), year
def write_version(filename=_ospath("sisl/info.py")):
version_str = '''# This file is automatically generated from sisl setup.py
released = {released}
# Git information (specific commit, etc.)
git_revision = "{git}"
git_revision_short = git_revision[:7]
git_count = {count}
# Version information
major = {version[0]}
minor = {version[1]}
micro = {version[2]}
version = ".".join(map(str,[major, minor, micro]))
release = version
if git_count > 2 and not released:
# Add git-revision to the version string
version += "+" + str(git_count)
# BibTeX information if people wish to cite
bibtex = f"""@misc{{{{zerothi_sisl,
author = {{{{Papior, Nick}}}},
title = {{{{sisl: v{{version}}}}}},
year = {{{{{rev_year}}}}},
doi = {{{{10.5281/zenodo.597181}}}},
url = {{{{https://doi.org/10.5281/zenodo.597181}}}},
}}}}"""
def cite():
return bibtex
'''
# If we are in git we try and fetch the
# git version as well
GIT_REV, GIT_COUNT, REV_YEAR = git_version()
with open(filename, "w") as fh:
fh.write(version_str.format(version=[MAJOR, MINOR, MICRO],
released=ISRELEASED,
count=GIT_COUNT,
rev_year=REV_YEAR, git=GIT_REV))
if __name__ == "__main__":
# First figure out if we should define the
# version file
if "only-version" in sys.argv:
# Figure out if we should write a specific file
print("Only creating the version file")
write_version()
sys.exit(0)
try:
# Create version file
# if allowed
write_version()
except Exception as e:
print("Could not write sisl/info.py:")
print(str(e))
if ISRELEASED:
metadata["version"] = VERSION
else:
metadata["version"] = VERSION + "-dev"
# Freeze to support parallel compilation when using spawn instead of fork
multiprocessing.freeze_support()
setup(**metadata)