pycapnp/setup.py

264 lines
8.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
pycapnp distutils setup.py
"""
import glob
2013-07-07 00:12:15 -07:00
import os
import shutil
import struct
import sys
import pkgconfig
from distutils.command.clean import clean as _clean
from setuptools import setup, Extension
_this_dir = os.path.dirname(__file__)
sys.path.insert(1, _this_dir)
from buildutils.build import build_libcapnp
from buildutils.bundle import fetch_libcapnp
MAJOR = 1
2023-01-26 16:43:40 -08:00
MINOR = 3
MICRO = 0
TAG = ""
VERSION = "%d.%d.%d%s" % (MAJOR, MINOR, MICRO, TAG)
# Write version info
def write_version_py(filename=None):
"""
Generate pycapnp version
"""
cnt = """\
from .lib.capnp import _CAPNP_VERSION_MAJOR as LIBCAPNP_VERSION_MAJOR # noqa: F401
from .lib.capnp import _CAPNP_VERSION_MINOR as LIBCAPNP_VERSION_MINOR # noqa: F401
from .lib.capnp import _CAPNP_VERSION_MICRO as LIBCAPNP_VERSION_MICRO # noqa: F401
from .lib.capnp import _CAPNP_VERSION as LIBCAPNP_VERSION # noqa: F401
version = '%s'
short_version = '%s'
"""
if not filename:
filename = os.path.join(os.path.dirname(__file__), "capnp", "version.py")
a = open(filename, "w")
try:
a.write(cnt % (VERSION, VERSION))
finally:
a.close()
2013-07-07 00:12:15 -07:00
write_version_py()
# Try to use README.md and CHANGELOG.md as description and changelog
with open("README.md", encoding="utf-8") as f:
long_description = f.read()
with open("CHANGELOG.md", encoding="utf-8") as f:
changelog = f.read()
changelog = "\nChangelog\n=============\n" + changelog
long_description += changelog
2013-08-12 12:39:35 -07:00
class clean(_clean):
"""
Clean command, invoked with `python setup.py clean`
"""
def run(self):
_clean.run(self)
for x in [
os.path.join("capnp", "lib", "capnp.cpp"),
os.path.join("capnp", "lib", "capnp.h"),
os.path.join("capnp", "version.py"),
"build",
"build32",
"build64",
"bundled",
] + glob.glob(os.path.join("capnp", "*.capnp")):
print("removing %s" % x)
try:
os.remove(x)
except OSError:
shutil.rmtree(x, ignore_errors=True)
from Cython.Distutils import build_ext as build_ext_c # noqa: E402
class build_libcapnp_ext(build_ext_c):
"""
Build capnproto library
"""
user_options = build_ext_c.user_options + [
("force-bundled-libcapnp", None, "Bundle capnp library into the installer"),
("force-system-libcapnp", None, "Use system capnp library"),
("libcapnp-url=", "u", "URL to download libcapnp from (only if bundled)"),
]
def initialize_options(self):
build_ext_c.initialize_options(self)
self.force_bundled_libcapnp = None
self.force_system_libcapnp = None
self.libcapnp_url = None
def finalize_options(self):
# print('The custom option for install is ', self.custom_option)
build_ext_c.finalize_options(self)
def build_extension(self, ext):
build_ext_c.build_extension(self, ext)
def run(self): # noqa: C901
if self.force_bundled_libcapnp:
need_build = True
elif self.force_system_libcapnp:
need_build = False
else:
# Try to use capnp executable to find include and lib path
capnp_executable = shutil.which("capnp")
if capnp_executable:
capnp_dir = os.path.dirname(capnp_executable)
self.include_dirs += [os.path.join(capnp_dir, "..", "include")]
self.library_dirs += [
os.path.join(
capnp_dir, "..", "lib{}".format(8 * struct.calcsize("P"))
)
]
self.library_dirs += [os.path.join(capnp_dir, "..", "lib")]
# Look for capnproto using pkg-config (and minimum version)
try:
if pkgconfig.installed("capnp", ">= 0.7.0"):
need_build = False
else:
need_build = True
except EnvironmentError:
# pkg-config not available in path
need_build = True
if need_build:
print(
"*WARNING* no libcapnp detected or rebuild forced. "
"Attempting to build it from source now. "
"If you have C++ Cap'n Proto installed, it may be out of date or is not being detected. "
"This may take a while..."
)
bundle_dir = os.path.join(_this_dir, "bundled")
if not os.path.exists(bundle_dir):
os.mkdir(bundle_dir)
build_dir = os.path.join(
_this_dir, "build{}".format(8 * struct.calcsize("P"))
)
if not os.path.exists(build_dir):
os.mkdir(build_dir)
# Check if we've already built capnproto
capnp_bin = os.path.join(build_dir, "bin", "capnp")
if os.name == "nt":
capnp_bin = os.path.join(build_dir, "bin", "capnp.exe")
if not os.path.exists(capnp_bin):
# Not built, fetch and build
fetch_libcapnp(bundle_dir, self.libcapnp_url)
build_libcapnp(bundle_dir, build_dir)
else:
print("capnproto already built at {}".format(build_dir))
self.include_dirs += [os.path.join(build_dir, "include")]
self.library_dirs += [
os.path.join(build_dir, "lib{}".format(8 * struct.calcsize("P")))
]
self.library_dirs += [os.path.join(build_dir, "lib")]
# Copy .capnp files from source
src_glob = glob.glob(os.path.join(build_dir, "include", "capnp", "*.capnp"))
dst_dir = os.path.join(self.build_lib, "capnp")
for file in src_glob:
print("copying {} -> {}".format(file, dst_dir))
shutil.copy(file, dst_dir)
return build_ext_c.run(self)
extra_compile_args = ["--std=c++14"]
extra_link_args = []
if os.name == "nt":
extra_compile_args = ["/std:c++14", "/MD"]
extra_link_args = ["/MANIFEST"]
import Cython.Build # noqa: E402
import Cython # noqa: E402
extensions = [
Extension(
"*",
Integrate the KJ event loop into Python's asyncio event loop (#310) * Integrate the KJ event loop into Python's asyncio event loop Fix #256 This PR attempts to remove the slow and expensive polling behavior for asyncio in favor of proper linking of the KJ event loop to the asyncio event loop. * Don't memcopy buffer * Improve promise cancellation and prepare for timer implementation * Add attribution for asyncProvider.cpp * Implement timeout * Cleanup * First round of simplifications * Add more a_wait functions and a shutdown function * Fix edge-cases with loop shutdown * Clean up calculator examples * Cleanup * Cleanup * Reformat * Fix warnings * Reformat again * Compatibility with macos * Inline the asyncio loop in some places where this is feasible * Add todo * Fix * Remove synchronous wait * Wrap fd listening callbacks in a class * Remove poll_forever * Remove the thread-local/thread-global optimization This will not matter much soon anyway, and simplifies things * Share promise code by using fused types * Improve refcounting of python objects in promises We replace many instances of PyObject* by Own<PyRefCounter> for more automatic reference management. * Code wrapPyFunc in a similar way to wrapPyFuncNoArg * Refactor capabilityHelper, fix several memory bugs for promises and add __await__ * Improve promise ownership, reduce memory leaks Promise wrappers now hold a Own<Promise<Own<PyRefCounter>>> object. This might seem like excessive nesting of objects (which to some degree it is, but with good reason): - The outer Own is needed because Cython cannot allocate objects without a nullary constructor on the stack (Promise doesn't have a nullary constructor). Additionally, I believe it would be difficult or impossible to detect when a promise is cancelled/moved if we use a bare Promise. - Every promise returns a Owned PyRefCounter. PyRefCounter makes sure that a reference to the returned object keeps existing until the promise is fulfilled or cancelled. Previously, this was attempted using attach, which is redundant and makes reasoning about PyINCREF and PyDECREF very difficult. - Because a promise holds a Own<Promise<...>>, when we perform any kind of action on that promise (a_wait, then, ...), we have to explicitly move() the ownership around. This will leave the original promise with a NULL-pointer, which we can easily detect as a cancelled promise. Promises now only hold references to their 'parents' when strictly needed. This should reduce memory pressure. * Simplify and test the promise joining functionality * Attach forgotten parent * Catch exceptions in add_reader and friends * Further cleanup of memory leaks * Get rid of a_wait() in examples * Cancel all fd read operations when the python asyncio loop is closed * Formatting * Remove support for capnp < 7000 * Bring asyncProvider.cpp more in line with upstream async-io-unix.c++ It was originally copied from the nodejs implementation, which in turn copied from async-io-unix.c++. But that copy is pretty old. * Fix a bug that caused file descriptors to never be closed * Implement AsyncIoStream based on Python transports and protocols * Get rid of asyncProvider All asyncio now goes through _AsyncIoStream * Formatting * Add __dict__ to PyAsyncIoStreamProtocol for python 3.7 * Reintroduce strange ipv4/ipv6 selection code to make ci happy * Extra pause_reading() * Work around more python bugs * Be careful to only close transport when this is still possible * Move pause_reading() workaround
2023-06-06 20:08:15 +02:00
[
"capnp/helpers/capabilityHelper.cpp",
"capnp/lib/*.pyx",
],
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
language="c++",
)
]
setup(
Use cibuildwheel in ci (#309) * Use cibuildwheel in ci `cibuildwheel` is a system that automatically compiles and repairs wheels for many python versions and architectures at once. This has some advantages vs the old situation: - Macosx wheels had inconsistent minimum versions ranging between 10.9 and 11.0. I'm not sure why this happens, but for some users this means they have to build from source on macosx. With cibuildwheel, the build is consistent, with 10.9 as the minimum for x86 and 11.0 for arm64. - Consolidation between the packaging tests and manylinux tests. - Addition of musllinux targets and additional cross-compilation to ppc64le and s390x. - With cibuildwheel, new python versions should be automatically picked up. - Separation of the sdist build and lint checks. There is not reason to run that many times. All possible build targets succeed, except for ARM64 on Windows. The upstream capnp build fails. I've disabled it. The cross-compilation builds on linux are pretty slow. This could potentially be sped up by separating the builds of manylinux and musllinux, but I'm not sure if it's worth the extra complexity. (One can also contemplate disabling these targets.) Tests for macosx arm64 cannot be run (but also couldn't be run in the previous system. This should be remedied once apple silicon becomes available on the CI. I've also added some commented-out code that can automatically take care of uploading a build to PyPi when a release is created. One might contemplate using this. * Set CMAKE_OSX_ARCHITECTURES for arm64 and disable universal2
2023-04-04 03:55:17 +02:00
python_requires=">=3.7",
name="pycapnp",
packages=["capnp"],
2013-08-12 11:56:44 -07:00
version=VERSION,
package_data={
"capnp": [
"*.pxd",
"*.h",
"*.capnp",
"helpers/*.pxd",
"helpers/*.h",
"helpers/*.cpp",
"includes/*.pxd",
"lib/*.pxd",
"lib/*.py",
"lib/*.pyx",
"lib/*.h",
"templates/*",
]
},
ext_modules=Cython.Build.cythonize(extensions),
cmdclass={"clean": clean, "build_ext": build_libcapnp_ext},
install_requires=[],
entry_points={"console_scripts": ["capnpc-cython = capnp._gen:main"]},
2013-08-12 11:56:44 -07:00
# PyPi info
description="A cython wrapping of the C++ Cap'n Proto library",
2013-08-12 12:39:35 -07:00
long_description=long_description,
long_description_content_type="text/markdown",
license="BSD",
# (setup.py only supports 1 author...)
author="Jacob Alexander", # <- Current maintainer; Original author -> Jason Paryani
author_email="haata@kiibohd.com",
url="https://github.com/capnproto/pycapnp",
download_url="https://github.com/haata/pycapnp/archive/v%s.zip" % VERSION,
keywords=["capnp", "capnproto", "Cap'n Proto", "pycapnp"],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows :: Windows 10",
"Operating System :: POSIX",
"Programming Language :: C++",
"Programming Language :: Cython",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Communications",
],
)