155 lines
5.4 KiB
Python
155 lines
5.4 KiB
Python
"""
|
|
Poetry build.py
|
|
|
|
See: https://stackoverflow.com/questions/60073711/how-to-build-c-extensions-via-poetry
|
|
"""
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
from distutils.command.build_py import build_py
|
|
from distutils.command.build_ext import build_ext
|
|
from distutils.errors import DistutilsPlatformError, CCompilerError, DistutilsExecError
|
|
from distutils.extension import Extension
|
|
|
|
from glob import glob
|
|
|
|
|
|
DIR = os.path.dirname(os.path.realpath(__file__))
|
|
ROOT_DIR = os.path.abspath(os.path.join(DIR, "..", ".."))
|
|
|
|
INCLUDE_DIRS = [
|
|
os.path.join(ROOT_DIR, "include"),
|
|
os.path.join(ROOT_DIR, "src"),
|
|
os.path.join(ROOT_DIR, "src/bindings/python"),
|
|
os.path.join(ROOT_DIR, "ext/concurrentqueue"),
|
|
os.path.join(ROOT_DIR, "ext/lwip/src/include"),
|
|
os.path.join(ROOT_DIR, "ext/lwip-contrib/ports/unix/port/include"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/include"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/ext"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/node"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/service"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/osdep"),
|
|
os.path.join(ROOT_DIR, "ext/ZeroTierOne/controller"),
|
|
]
|
|
|
|
|
|
class LibztModule(Extension):
|
|
"""Libzt extension module"""
|
|
def __init__(self):
|
|
sources = [
|
|
*glob(os.path.join(ROOT_DIR, "src/bindings/python/*.cxx")),
|
|
*glob(os.path.join(ROOT_DIR, "src/bindings/python/zt.i")),
|
|
*glob(os.path.join(ROOT_DIR, "src/*.cpp")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/node/*.cpp")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/osdep/OSUtils.cpp")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/osdep/PortMapper.cpp")),
|
|
]
|
|
# noinspection PyUnresolvedReferences
|
|
assert len(sources) > 0, "no sources found"
|
|
|
|
# noinspection PyTypeChecker
|
|
super().__init__(
|
|
"libzt._libzt",
|
|
sources=sources,
|
|
include_dirs=INCLUDE_DIRS,
|
|
# depends = ... # TODO: specify dependencies for recompile
|
|
extra_compile_args=[
|
|
"-std=c++11",
|
|
"-DZTS_ENABLE_PYTHON=1",
|
|
"-DZT_SDK",
|
|
"-Wno-parentheses-equality",
|
|
"-Wno-macro-redefined",
|
|
"-Wno-tautological-overlap-compare",
|
|
"-Wno-tautological-constant-out-of-range-compare",
|
|
],
|
|
swig_opts=[
|
|
"-c++",
|
|
f"-I{os.path.join(ROOT_DIR, 'include')}",
|
|
],
|
|
)
|
|
|
|
|
|
def cstuff():
|
|
"""C library"""
|
|
sources = [
|
|
# libnatpmp
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/ext/libnatpmp/natpmp.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/ext/libnatpmp/wingettimeofday.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/ZeroTierOne/ext/libnatpmp/getgateway.c")),
|
|
# miniupnpc
|
|
*glob(os.path.join(ROOT_DIR, "ext/miniupnpc/*.c")),
|
|
# lwip
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/netif/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/api/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/core/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/core/ipv4/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/core/ipv6/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip/src/netif/*.c")),
|
|
*glob(os.path.join(ROOT_DIR, "ext/lwip-contrib/ports/unix/port/sys_arch.c")),
|
|
]
|
|
# noinspection PyUnresolvedReferences
|
|
assert len(sources) > 0, "no sources"
|
|
|
|
return ("cstuff", {
|
|
"sources": sources,
|
|
"include_dirs": INCLUDE_DIRS,
|
|
})
|
|
|
|
|
|
def copy_python_files(src_dir: str, dst_dir: str):
|
|
""" Copy all Python files from `src_dir` to `dst_dir`"""
|
|
for filename in {os.path.basename(filepath) for filepath in glob(os.path.join(src_dir, "*.py"))}:
|
|
shutil.copy(os.path.join(src_dir, filename), os.path.join(dst_dir, filename))
|
|
|
|
|
|
class BuildFailed(Exception):
|
|
pass
|
|
|
|
|
|
class PyBuilder(build_py):
|
|
|
|
def run(self):
|
|
# Build extension module first since libzt.py is generated by SWIG
|
|
self.run_command("build_clib")
|
|
self.run_command("build_ext")
|
|
return super().run()
|
|
|
|
def build_packages(self):
|
|
# Copy Python files into packaging directory
|
|
copy_python_files(src_dir=os.path.join(ROOT_DIR, "src/bindings/python"),
|
|
dst_dir=os.path.join(DIR, "libzt"))
|
|
super().build_packages()
|
|
|
|
|
|
class ExtBuilder(build_ext):
|
|
|
|
def run(self):
|
|
try:
|
|
super().run()
|
|
except (DistutilsPlatformError, FileNotFoundError):
|
|
raise BuildFailed('File not found. Could not compile C extension.')
|
|
|
|
def build_extension(self, ext):
|
|
try:
|
|
super().build_extension(ext)
|
|
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
|
|
raise BuildFailed('Could not compile C extension.')
|
|
|
|
|
|
def build(setup_kwargs):
|
|
"""Build """
|
|
|
|
assert os.getcwd() == DIR, f"must be invoked from {DIR}"
|
|
|
|
# Ensure git submodules are loaded
|
|
subprocess.run(["git", "submodule", "update", "--init"])
|
|
|
|
# LICENSE file
|
|
shutil.copy(os.path.join(ROOT_DIR, "LICENSE.txt"), os.path.join(DIR, "LICENSE"))
|
|
|
|
# Pass up extensions information
|
|
setup_kwargs["libraries"] = [cstuff()]
|
|
setup_kwargs["ext_modules"] = [LibztModule()]
|
|
setup_kwargs["cmdclass"] = {"build_py": PyBuilder, "build_ext": ExtBuilder}
|