1#!/usr/bin/python3 2import os 3import re 4import sys 5import platform 6import subprocess 7 8from setuptools import setup, setuptools, Extension 9from setuptools.command.build_ext import build_ext 10from distutils.version import LooseVersion 11 12 13class CMakeExtension(Extension): 14 def __init__(self, name, sourcedir=""): 15 Extension.__init__(self, name, sources=["./"]) 16 self.sourcedir = os.path.abspath(sourcedir) 17 18 19class CMakeBuild(build_ext): 20 def run(self): 21 try: 22 out = subprocess.check_output(["cmake", "--version"]) 23 except OSError: 24 raise RuntimeError( 25 "CMake must be installed to build" 26 + " the following extensions: " 27 + ", ".join(e.name for e in self.extensions) 28 ) 29 30 if platform.system() == "Windows": 31 cmake_version = LooseVersion( 32 re.search(r"version\s*([\d.]+)", out.decode()).group(1) 33 ) 34 if cmake_version < "3.1.0": 35 raise RuntimeError("CMake >= 3.1.0 is required on Windows") 36 37 for ext in self.extensions: 38 self.build_extension(ext) 39 40 def build_extension(self, ext): 41 extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 42 cmake_args = [ 43 "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + str(extdir), 44 "-DPYTHON_EXECUTABLE=" + sys.executable, 45 ] 46 47 cfg = "Debug" if self.debug else "Release" 48 build_args = ["--config", cfg] 49 50 if platform.system() == "Windows": 51 cmake_args += [ 52 "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) 53 ] 54 if sys.maxsize > 2 ** 32: 55 cmake_args += ["-A", "x64"] 56 build_args += ["--", "/m"] 57 else: 58 cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] 59 build_args += ["--", "-j", "6"] 60 61 env = os.environ.copy() 62 env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( 63 env.get("CXXFLAGS", ""), self.distribution.get_version() 64 ) 65 if not os.path.exists(self.build_temp): 66 os.makedirs(self.build_temp) 67 subprocess.check_call( 68 ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env 69 ) 70 subprocess.check_call( 71 ["cmake", "--build", "."] + build_args, cwd=self.build_temp 72 ) 73 74 75class get_pybind_include(object): 76 """Helper class to determine the pybind11 include path 77 78 The purpose of this class is to postpone importing pybind11 79 until it is actually installed, so that the ``get_include()`` 80 method can be invoked.""" 81 82 def __init__(self, user=False): 83 self.user = user 84 85 def __str__(self): 86 import pybind11 87 88 return pybind11.get_include(self.user) 89 90 91ext_modules = [ 92 Extension( 93 "chiapos", 94 [ 95 "lib/FiniteStateEntropy/lib/entropy_common.c", 96 "lib/FiniteStateEntropy/lib/fse_compress.c", 97 "lib/FiniteStateEntropy/lib/fse_decompress.c", 98 "lib/FiniteStateEntropy/lib/hist.c", 99 "python-bindings/chiapos.cpp", 100 "uint128_t/uint128_t.cpp", 101 "src/b3/blake3.c", 102 "src/b3/blake3_portable.c", 103 "src/b3/blake3_dispatch.c", 104 "src/b3/blake3_avx2.c", 105 "src/b3/blake3_avx512.c", 106 "src/b3/blake3_sse41.c", 107 "src/chacha8.c", 108 ], 109 include_dirs=[ 110 "src", 111 "uint128_t", 112 ".", 113 ], 114 ), 115] 116 117 118# As of Python 3.6, CCompiler has a `has_flag` method. 119# cf http://bugs.python.org/issue26689 120def has_flag(compiler, flagname): 121 """Return a boolean indicating whether a flag name is supported on 122 the specified compiler. 123 """ 124 import tempfile 125 126 with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f: 127 f.write("int main (int argc, char **argv) { return 0; }") 128 try: 129 compiler.compile([f.name], extra_postargs=[flagname]) 130 except setuptools.distutils.errors.CompileError: 131 return False 132 return True 133 134 135def cpp_flag(compiler): 136 """Return the -std=c++[11/14/17] compiler flag. 137 138 The newer version is prefered over c++11 (when it is available). 139 """ 140 flags = ["-std=c++17", "-std=c++14", "-std=c++11"] 141 142 for flag in flags: 143 if has_flag(compiler, flag): 144 return flag 145 146 raise RuntimeError("Unsupported compiler -- at least C++11 support " "is needed!") 147 148 149class BuildExt(build_ext): 150 """A custom build extension for adding compiler-specific options.""" 151 152 c_opts = { 153 "msvc": ["/EHsc", "/std:c++17", "/O2"], 154 "unix": [""], 155 } 156 l_opts = { 157 "msvc": [], 158 "unix": [""], 159 } 160 161 if sys.platform == "darwin": 162 darwin_opts = ["-stdlib=libc++", "-mmacosx-version-min=10.14"] 163 c_opts["unix"] += darwin_opts 164 l_opts["unix"] += darwin_opts # type: ignore 165 166 def build_extensions(self): 167 ct = self.compiler.compiler_type 168 opts = self.c_opts.get(ct, []) 169 link_opts = self.l_opts.get(ct, []) 170 if ct == "unix": 171 opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) 172 opts.append(cpp_flag(self.compiler)) 173 if has_flag(self.compiler, "-fvisibility=hidden"): 174 opts.append("-fvisibility=hidden") 175 elif ct == "msvc": 176 opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) 177 for ext in self.extensions: 178 ext.extra_compile_args = opts 179 ext.extra_link_args = link_opts 180 build_ext.build_extensions(self) 181 182 183if platform.system() == "Windows": 184 setup( 185 name="chiapos", 186 author="Mariano Sorgente", 187 author_email="mariano@chia.net", 188 description="Chia proof of space plotting, proving, and verifying (wraps C++)", 189 license="Apache License", 190 python_requires=">=3.7", 191 long_description=open("README.md").read(), 192 long_description_content_type="text/markdown", 193 url="https://github.com/Chia-Network/chiapos", 194 setup_requires=["pybind11"], 195 ext_modules=ext_modules, 196 cmdclass={"build_ext": BuildExt}, 197 zip_safe=False, 198 ) 199else: 200 setup( 201 name="chiapos", 202 version="1.0.3", 203 author="Mariano Sorgente", 204 author_email="mariano@chia.net", 205 description="Chia proof of space plotting, proving, and verifying (wraps C++)", 206 license="Apache License", 207 python_requires=">=3.7", 208 long_description=open("README.md").read(), 209 long_description_content_type="text/markdown", 210 url="https://github.com/Chia-Network/chiapos", 211 cmdclass=dict(build_ext=CMakeBuild), 212 zip_safe=False, 213 ) 214