-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup.py
114 lines (98 loc) · 3.85 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
import os
import pathlib
import sys
import setuptools
import distutils.extension
import distutils.command.build_ext
# We subclass the build_ext command to build executables instead of building
# python extensions based on [1].
# This is sort of a hack to trick setuptools into thinking that the package is
# not pure. Otherwise, if we simply compile the binaries and put them into
# place when we build the wheel, the wheel gets improperly labeled with a
# "Root-is-purelib: true" tag, then auditwheel pukes when it finds compiled
# binaries in a purelib directory (see [2]).
#
# Cons of this strategy:
# - it is far more opaque than just compiling the files ourselves
# - it is abusing setuptools/distutils to do something other than intended
#
# Pros of this strategy:
# - we get to use cibuildwheel to build a ton of wheels in a github action
# - it seems to pick up the compiler pretty reliably, even on windows
# - it is the only way I know how to get a PEP-427-compliant wheel
#
# [1] github.com/pypa/packaging-problems/issues/542#issuecomment-912838470
# [2] peps.python.org/pep-0427/#what-s-the-deal-with-purelib-vs-platlib
ext_modules = [
distutils.extension.Extension("mkninja.findglob", ["findglob/main.c"]),
distutils.extension.Extension("mkninja.manifest", ["manifest/manifest.c"]),
distutils.extension.Extension("mkninja.stamp", ["stamp/stamp.c"]),
]
class build_exe(distutils.command.build_ext.build_ext):
"""
Subclass the build_ext command so we can compile executables instead of
shared objects.
"""
## allow the default .run() to configure and setup the compiler.
# def run(self):
# ...
def build_extensions(self):
for ext in self.extensions:
objs = self.compiler.compile(
ext.sources,
output_dir=self.build_temp,
debug=self.debug,
extra_postargs=self.compile_postargs(),
)
self.compiler.link_executable(
objs,
self.get_executable_output(ext),
debug=self.debug,
target_lang="c",
)
def compile_postargs(self):
if sys.platform == "win32":
return [
# optimize for speed
"/O2",
# only Wall is higher than W4
"/W4",
# treat warnings as errors
"/WX",
# /wd4221, /wd4204: we don't care about ansi compliance
"/wd4221",
"/wd4204",
]
return ["-Wall", "-Wextra", "-Werror", "-O3"]
def get_executable_output(self, ext):
return os.path.join(self.build_lib, *ext.name.split("."))
def get_outputs(self):
return [self.get_executable_output(ext) for ext in self.extensions]
if __name__ == "__main__":
HERE = pathlib.Path(__file__).parent
with (HERE / "mkninja" / "__init__.py").open() as f:
firstline = next(iter(f))
assert firstline.startswith("__version__ = ")
version = firstline.split('"')[1]
readme = (HERE / "README.md").read_text()
setup_args = dict(
name="mkninja",
version=version,
author="Rex Roni",
author_email="[email protected]",
description="A python front-end for ninja",
long_description=readme,
long_description_content_type="text/markdown",
url="https://github.com/rexroni/mkninja",
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: The Unlicense (Unlicense)",
"Operating System :: OS Independent",
],
packages=["mkninja"],
python_requires=">=3.6",
entry_points={"console_scripts": ["mkninja = mkninja.__main__:main"]},
ext_modules=ext_modules,
cmdclass={"build_ext": build_exe},
)
setuptools.setup(**setup_args)