diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..18d6149 --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,50 @@ +name: Wheels + +on: + workflow_dispatch: + pull_request: + push: + tags: ["v*"] + +jobs: + build_sdist: + name: Build sdist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - run: uv build --sdist + - uses: actions/upload-artifact@v4 + with: + name: sdist + path: dist/*.tar.gz + + build_wheels: + name: Wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest] + steps: + - uses: actions/checkout@v4 + - uses: pypa/cibuildwheel@v3.2 + - uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.os }} + path: wheelhouse/*.whl + + publish: + name: Publish to PyPI + needs: [build_sdist, build_wheels] + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index b0069c9..c2eead5 100644 --- a/.gitignore +++ b/.gitignore @@ -201,9 +201,9 @@ Thumbs.db *.egg-info/ .vscode/ -include/ -lib/ - tmp/ -*.pkl \ No newline at end of file +*.pkl + +# Vendored prebuilt SDK binaries are intentionally tracked +!vendor/** \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/CLAUDE.md b/CLAUDE.md index 6cedca5..9630c73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,41 +23,27 @@ Key components: ## Build Commands -### Ubuntu/Linux Setup and Build -```bash -# Full setup (downloads dependencies and builds) -bash setup_ubuntu.sh - -# Manual build after setup -python setup.py install - -# Clean build artifacts -python setup.py clean -``` +The project is uv-first and uses scikit-build-core as its build backend. +Prebuilt PXREARobotSDK binaries are vendored in `vendor/` (Linux x86_64, +Linux aarch64, Windows amd64), so no SDK download or build step is needed. +Building the extension is only possible on Linux and Windows; macOS is +unsupported (no vendor binaries exist). -### Windows Setup and Build -```batch -# Full setup (downloads dependencies and builds) -setup_windows.bat - -# Manual build after setup -python setup.py install -``` - -### Development Commands ```bash -# Uninstall existing package -pip uninstall -y xrobotoolkit_sdk +# Create .venv (Python from .python-version), build and install the package +uv sync -# Install pybind11 dependency -conda install -c conda-forge pybind11 -# or -pip install pybind11 +# Run an example inside the project venv +uv run examples/example.py -# Build and install -python setup.py install +# Build sdist + wheel +uv build ``` +Release wheels are built by cibuildwheel in `.github/workflows/wheels.yml` +(manylinux_2_34 for both Linux arches, dictated by the vendored libraries' +glibc requirements — see `vendor/README.md`). + ## Data Flow and Threading The SDK uses a callback-based architecture: @@ -84,13 +70,12 @@ The SDK uses a callback-based architecture: ## Dependencies -### Required -- pybind11 (Python binding framework) -- CMake (build system) -- XRoboToolkit-PC-Service SDK (automatically downloaded during setup) +### Required (build time) +- CMake >= 3.18 and a C++17 compiler +- pybind11 and scikit-build-core (fetched automatically by the build backend) -### Platform-specific Libraries -- Linux: `libPXREARobotSDK.so` +### Platform-specific Libraries (vendored in `vendor/`) +- Linux: `libPXREARobotSDK.so` (x86_64 and aarch64 variants) - Windows: `PXREARobotSDK.dll` and `PXREARobotSDK.lib` ## Testing diff --git a/CMakeLists.txt b/CMakeLists.txt index 654b7b6..e4a171c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,54 +1,43 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.18) -project(MyPybind11Project LANGUAGES CXX) +project(xrobotoolkit_sdk LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -cmake_host_system_information(RESULT ISA_NAME QUERY OS_PLATFORM) # Added: Important for UNIX specific logic -message(STATUS "OS_PLATFORM (ISA_NAME): ${ISA_NAME}") +find_package(pybind11 CONFIG REQUIRED) -include(GNUInstallDirs) # Add this line -find_package(pybind11 REQUIRED) - -# Python Bindings for py_bindings.cpp -pybind11_add_module(xrobotoolkit_sdk MODULE bindings/py_bindings.cpp) - -# Link xrobotoolkit_sdk module against pybind11 -target_link_libraries(xrobotoolkit_sdk PRIVATE pybind11::module) - -# Add include directories and link libraries for PXREARobotSDK to xrobotoolkit_sdk target +# --- Select the vendored prebuilt PXREARobotSDK for the target platform --- +# Binaries are checked into vendor/ (see vendor/README.md for provenance). if(WIN32) - target_include_directories(xrobotoolkit_sdk PUBLIC - ${PROJECT_SOURCE_DIR}/include - ) - target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib) - target_link_libraries(xrobotoolkit_sdk PUBLIC - PXREARobotSDK.dll # Assuming this is how PXREARobotSDK is linked, mirroring ConsoleDemo - ) -endif() - -if(UNIX) - # ISA_NAME is set by cmake_host_system_information above - if(ISA_NAME STREQUAL "aarch64") - target_include_directories(xrobotoolkit_sdk PUBLIC - ${PROJECT_SOURCE_DIR}/include/aarch64 - ) - target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib/aarch64) + set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/windows_amd64") + set(XRT_VENDOR_IMPLIB "${XRT_VENDOR_DIR}/PXREARobotSDK.lib") + set(XRT_VENDOR_RUNTIME "${XRT_VENDOR_DIR}/PXREARobotSDK.dll") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$") + set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/linux_aarch64") else() - target_include_directories(xrobotoolkit_sdk PUBLIC - ${PROJECT_SOURCE_DIR}/include - ) - target_link_directories(xrobotoolkit_sdk PUBLIC ${PROJECT_SOURCE_DIR}/lib) + set(XRT_VENDOR_DIR "${PROJECT_SOURCE_DIR}/vendor/linux_x86_64") endif() - target_link_libraries(xrobotoolkit_sdk PUBLIC - PXREARobotSDK - ) + set(XRT_VENDOR_IMPLIB "${XRT_VENDOR_DIR}/libPXREARobotSDK.so") + set(XRT_VENDOR_RUNTIME "${XRT_VENDOR_DIR}/libPXREARobotSDK.so") +else() + message(FATAL_ERROR + "Unsupported platform '${CMAKE_SYSTEM_NAME}'. PXREARobotSDK ships prebuilt " + "binaries for Linux (x86_64, aarch64) and Windows (amd64) only.") +endif() + +pybind11_add_module(_core MODULE bindings/py_bindings.cpp) + +target_include_directories(_core PRIVATE "${PROJECT_SOURCE_DIR}/vendor/include") +target_link_libraries(_core PRIVATE "${XRT_VENDOR_IMPLIB}") + +# The extension and the vendored shared library are installed side by side in +# the package directory; $ORIGIN makes the dynamic linker look there first. +# (On Windows, __init__.py calls os.add_dll_directory instead.) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set_target_properties(_core PROPERTIES INSTALL_RPATH "$ORIGIN") endif() -# Install the Python module -install(TARGETS xrobotoolkit_sdk - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # Installs to /lib - # You might want a Python-specific path like: - # DESTINATION lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages -) +install(TARGETS _core LIBRARY DESTINATION xrobotoolkit_sdk) +install(FILES "${XRT_VENDOR_RUNTIME}" DESTINATION xrobotoolkit_sdk) diff --git a/README.md b/README.md index 9d7a937..95a1557 100644 --- a/README.md +++ b/README.md @@ -4,49 +4,45 @@ This project provides a python interface to extract XR state using XRoboToolkit- ## Requirements -- [`pybind11`](https://github.com/pybind/pybind11) -- [`XRoboRoolkit PC Service`](https://github.com/XR-Robotics/XRoboToolkit-PC-Service#) +- Linux (x86_64 or aarch64, e.g. NVIDIA Orin) or Windows (x64), Python >= 3.10 +- A running [XRoboToolkit PC Service](https://github.com/XR-Robotics/XRoboToolkit-PC-Service) with a connected PICO headset (at runtime) -## Building the Project -### Ubuntu 22.04 +The prebuilt `PXREARobotSDK` binaries are vendored in `vendor/`, so no manual +SDK download or build is needed. +## Installation + +### From PyPI (once published) + +```bash +uv add xrobotoolkit-sdk +# or +pip install xrobotoolkit-sdk ``` -conda remove --name xr --all -conda create -n xr python=3.10 -conda activate xr - -mkdir -p tmp -cd tmp -git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git -cd XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK -bash build.sh -cd ../../../.. - -mkdir -p lib -mkdir -p include -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h include/ -cp -r tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann include/nlohmann/ -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so lib/ -# rm -rf tmp - -# Build the project -conda install -c conda-forge pybind11 - -pip uninstall -y xrobotoolkit_sdk -python setup.py install -``` -### Linux Ubuntu 22.04 arm64 version (Nvidia orin supported) -``` -bash setup_orin.sh -``` -### Windows -**Ensure pybind11 is installed before running the following command.** +### From source +If you do not have [uv](https://docs.astral.sh/uv/) installed, run: + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh ``` -setup_windows.bat + +Then (requires CMake and a C++17 compiler): + +```bash +git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind.git +cd XRoboToolkit-PC-Service-Pybind +uv sync ``` +`uv sync` creates `.venv/` with the Python version pinned in `.python-version`, +builds the extension, and installs the package into it. Run scripts with e.g. +`uv run examples/example.py`. + +To build distributable artifacts locally: `uv build` (wheels for Linux/Windows +are also built in CI via cibuildwheel, see `.github/workflows/wheels.yml`). + ## Using the Python Bindings **1. Get Controller and Headset Poses** diff --git a/bindings/py_bindings.cpp b/bindings/py_bindings.cpp index 4f7c9d9..9f0a6e4 100644 --- a/bindings/py_bindings.cpp +++ b/bindings/py_bindings.cpp @@ -495,7 +495,7 @@ int SendBytesToDeviceWrapper(const std::string& dev_id, pybind11::bytes blob) { } -PYBIND11_MODULE(xrobotoolkit_sdk, m) { +PYBIND11_MODULE(_core, m) { m.def("init", &init, "Initialize the PXREARobot SDK."); m.def("close", &deinit, "Deinitialize the PXREARobot SDK."); m.def("get_left_controller_pose", &getLeftControllerPose, "Get the left controller pose."); diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e464a3a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[build-system] +requires = ["scikit-build-core>=0.11", "pybind11>=2.13"] +build-backend = "scikit_build_core.build" + +[project] +name = "xrobotoolkit-sdk" +version = "1.1.0" +description = "Python bindings for the XRoboToolkit PC Service SDK (PICO XR controller, hand, and body tracking)." +readme = "README.md" +license = "MIT" +license-files = ["LICENSE"] +authors = [{ name = "Zhigen Zhao", email = "zhigen.zhao@bytedance.com" }] +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: C++", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Scientific/Engineering", +] + +[project.urls] +Homepage = "https://github.com/XR-Robotics/XRoboToolkit-PC-Service-Pybind" +Upstream = "https://github.com/XR-Robotics/XRoboToolkit-PC-Service" + +[tool.scikit-build] +minimum-version = "build-system.requires" +wheel.packages = ["src/xrobotoolkit_sdk"] + +# Rebuild the extension when any of these change (uv caches builds otherwise). +[tool.uv] +cache-keys = [ + { file = "pyproject.toml" }, + { file = "CMakeLists.txt" }, + { file = "bindings/**" }, + { file = "vendor/**" }, +] + +[tool.cibuildwheel] +archs = ["auto64"] +# Skip musl (no vendored musl lib) and free-threaded CPython (pybind11 +# free-threading support is experimental; Windows link also fails on it). +skip = "*-musllinux* cp3*t-*" +test-command = 'python -c "import xrobotoolkit_sdk"' + +[tool.cibuildwheel.linux] +# The vendored aarch64 libPXREARobotSDK.so requires glibc >= 2.34, the x86_64 +# one glibc >= 2.29, so manylinux_2_34 is the oldest tag both can claim. +manylinux-x86_64-image = "manylinux_2_34" +manylinux-aarch64-image = "manylinux_2_34" +# libPXREARobotSDK.so is already bundled in the package with rpath $ORIGIN; +# exclude it so auditwheel does not graft a second copy into .libs. +repair-wheel-command = "auditwheel repair --exclude libPXREARobotSDK.so -w {dest_dir} {wheel}" diff --git a/setup.py b/setup.py deleted file mode 100644 index 0cd2b10..0000000 --- a/setup.py +++ /dev/null @@ -1,148 +0,0 @@ -import os -import platform -import re -import shutil # Added for shutil.rmtree -import subprocess -import sys -from distutils.version import LooseVersion - -from setuptools import Command, Extension, find_packages, setup # Added Command -from setuptools.command.build_ext import build_ext - - -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=""): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - def run(self): - try: - out = subprocess.check_output(["cmake", "--version"]) - except OSError: - raise RuntimeError( - "CMake must be installed to build the following extensions: " - + ", ".join(e.name for e in self.extensions) - ) - - if platform.system() == "Windows": - cmake_version = LooseVersion(re.search(r"version\s*([\d.]+)", out.decode()).group(1)) - if cmake_version < "3.1.0": - raise RuntimeError("CMake >= 3.1.0 is required on Windows") - - for ext in self.extensions: - self.build_extension(ext) - - def build_extension(self, ext): - extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) - # required for auto-detection of auxiliary "native" libs - if not extdir.endswith(os.path.sep): - extdir += os.path.sep - - # Get pybind11 include paths - cmake_args = [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, - "-DPYTHON_EXECUTABLE=" + sys.executable, - "-DCMAKE_BUILD_TYPE=Release", - ] - - cfg = "Debug" if self.debug else "Release" - build_args = ["--config", cfg] - - if platform.system() == "Windows": - cmake_args += ["-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir)] - if sys.maxsize > 2**32: - cmake_args += ["-A", "x64"] - build_args += ["--", "/m"] - else: - cmake_args += ["-DCMAKE_BUILD_TYPE=" + cfg] - build_args += ["--", "-j2"] # Adjust core count as needed - - env = os.environ.copy() - env["CXXFLAGS"] = '{} -DVERSION_INFO=\\"{}\\"'.format( - env.get("CXXFLAGS", ""), self.distribution.get_version() - ) - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - - subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) - subprocess.check_call(["cmake", "--build", "."] + build_args, cwd=self.build_temp) - - -# New Clean Command -class CleanCommand(Command): - """Custom clean command to tidy up the project root.""" - - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - # Remove build directory - if os.path.exists("build"): - print("Removing 'build/' directory") - shutil.rmtree("build") - # Remove .egg-info directory - for item in os.listdir("."): - if item.endswith(".egg-info"): - print(f"Removing '{item}' directory") - shutil.rmtree(item) - for item in os.listdir("."): - if item.endswith(".eggs"): - print(f"Removing '{item}' directory") - shutil.rmtree(item) - # Optionally, remove dist directory if you generate distributions - if os.path.exists("dist"): - print("Removing 'dist/' directory") - shutil.rmtree("dist") - - -# New Uninstall Command -class UninstallCommand(Command): - """Custom command to uninstall the package.""" - - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - package_name = self.distribution.get_name() - print(f"Attempting to uninstall {package_name}...") - try: - subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", package_name]) - print(f"{package_name} uninstalled successfully.") - except subprocess.CalledProcessError as e: - print( - f"Failed to uninstall {package_name}. It may not be installed or pip uninstall failed." - ) - print(f"Error: {e}") - except FileNotFoundError: - print("pip command not found. Please ensure pip is installed and in your PATH.") - - -setup( - name="xrobotoolkit_sdk", - version="1.0.2", - author="Zhigen Zhao", - author_email="zhigen.zhao@bytedance.com", - description="A Python binding for XRobotoolkit PC Service SDK using pybind11 and CMake", - long_description="", # Optionally, load from a README.md file - ext_modules=[CMakeExtension("xrobotoolkit_sdk")], - cmdclass=dict( - build_ext=CMakeBuild, - clean=CleanCommand, # Add clean command - uninstall=UninstallCommand, # Add uninstall command - ), - zip_safe=False, - python_requires=">=3.10", # Specify your Python version requirement - packages=find_packages(), # If you have other Python packages in your project -) diff --git a/setup_orin.sh b/setup_orin.sh deleted file mode 100644 index 5d7476f..0000000 --- a/setup_orin.sh +++ /dev/null @@ -1,27 +0,0 @@ -if [[ "$CONDA_DEFAULT_ENV" != "" ]]; then - conda install -c conda-forge libstdcxx-ng -y -fi - -mkdir -p tmp -cd tmp -git clone -b orin https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git -cd XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK -bash build.sh -cd ../../../.. - -mkdir -p lib/aarch64 -mkdir -p include/aarch64 -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h include/aarch64/ -cp -r tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann include/aarch64/nlohmann/ -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so lib/aarch64/ -# rm -rf tmp - -# Build the project -if [[ "$CONDA_DEFAULT_ENV" != "" ]]; then - conda install -c conda-forge pybind11 -y -else - pip install pybind11 -y -fi - -pip uninstall -y xrobotoolkit_sdk -python setup.py install \ No newline at end of file diff --git a/setup_ubuntu.sh b/setup_ubuntu.sh deleted file mode 100644 index 327cc93..0000000 --- a/setup_ubuntu.sh +++ /dev/null @@ -1,27 +0,0 @@ -if [[ "$CONDA_DEFAULT_ENV" != "" ]]; then - conda install -c conda-forge libstdcxx-ng -y -fi - -mkdir -p tmp -cd tmp -git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git -cd XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK -bash build.sh -cd ../../../.. - -mkdir -p lib -mkdir -p include -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h include/ -cp -r tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann include/nlohmann/ -cp tmp/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so lib/ -# rm -rf tmp - -# Build the project -if [[ "$CONDA_DEFAULT_ENV" != "" ]]; then - conda install -c conda-forge pybind11 -y -else - pip install pybind11 -y -fi - -pip uninstall -y xrobotoolkit_sdk -python setup.py install \ No newline at end of file diff --git a/setup_windows.bat b/setup_windows.bat deleted file mode 100644 index 26de589..0000000 --- a/setup_windows.bat +++ /dev/null @@ -1,242 +0,0 @@ -@echo off -setlocal - -echo Setting up environment for XRoboToolkit-PC-Service... - -:: Define the base directory for the script execution -set "SCRIPT_ROOT=%CD%" - -:: Define a temporary directory for cloning -set "TEMP_DIR=tmp" - -:: Define source paths relative to the cloned repository root -set "XROBOTKIT_CLONED_REPO_PATH=%TEMP_DIR%\XRoboToolkit-PC-Service" -set "PXREAROBOTSDK_SOURCE_DIR=%XROBOTKIT_CLONED_REPO_PATH%\RoboticsService\PXREARobotSDK" -set "PXREAROBOTSDK_LIB_DIR=%XROBOTKIT_CLONED_REPO_PATH%\RoboticsService\SDK\win\64" - -:: Define destination directories -set "LIB_DEST_DIR=%SCRIPT_ROOT%\lib" -set "INCLUDE_DEST_DIR=%SCRIPT_ROOT%\include" - -:: Create destination directories -echo Creating destination directories... -mkdir "%LIB_DEST_DIR%" 2>NUL -if not exist "%LIB_DEST_DIR%" ( - echo Error: Failed to create lib directory. Exiting. - exit /b 1 -) - -mkdir "%INCLUDE_DEST_DIR%" 2>NUL -if not exist "%INCLUDE_DEST_DIR%" ( - echo Error: Failed to create include directory. Exiting. - exit /b 1 -) - -echo Destination directories created successfully. - -:: --- Check for pybind11 and install if not found --- -echo. -echo Checking for pybind11... -pip show pybind11 >NUL 2>&1 -if %errorlevel% neq 0 ( - echo Error: pybind11 not found. Please run `pip install pybind11` first. Exiting. - exit /b 1 -) - -:: --- Set PYBIND11_DIR for CMake --- -echo. -echo Setting PYBIND11_DIR environment variable... -for /f "usebackq" %%i in (`python -c "import sys; print(sys.prefix)"`) do set PYTHON_PREFIX=%%i -if not defined PYTHON_PREFIX ( - echo Error: Could not determine Python installation prefix. - echo Please ensure Python is correctly installed and in your PATH. Exiting. - exit /b 1 -) - -set "PYBIND11_DIR=%PYTHON_PREFIX%\Lib\site-packages\pybind11\share\cmake\pybind11" -echo Attempting to set PYBIND11_DIR to: %PYBIND11_DIR% -set PYBIND11_DIR=%PYBIND11_DIR% -if not exist "%PYBIND11_DIR%\pybind11Config.cmake" ( - echo Warning: pybind11Config.cmake not found at expected PYBIND11_DIR: "%PYBIND11_DIR%" - echo This might indicate a problem with the pybind11 installation or its path. - :: Attempting to find another common path if the standard one doesn't work. - for /d %%d in ("%PYTHON_PREFIX%\Lib\site-packages\pybind11\share\cmake\*") do ( - if exist "%%d\pybind11Config.cmake" ( - set "PYBIND11_DIR=%%d" - echo Found pybind11Config.cmake in "%%d". Using this path. - goto :pybind11_dir_found - ) - ) - echo Critical Error: pybind11Config.cmake could not be found after pybind11 installation. - echo Please check your pybind11 installation. Exiting. - exit /b 1 -) -:pybind11_dir_found -echo PYBIND11_DIR set to: %PYBIND11_DIR% - -set "DLL_NAME=PXREARobotSDK.dll" -set "LIB_NAME=PXREARobotSDK.lib" - -:: Create the temporary directory and navigate into it -echo Creating temporary directory: %TEMP_DIR% -mkdir %TEMP_DIR% -if not exist %TEMP_DIR% ( - echo Error: Failed to create temporary directory %TEMP_DIR%. Ingore. -) -cd %TEMP_DIR% -if %errorlevel% neq 0 ( - echo Error: Failed to navigate into %TEMP_DIR%. Exiting. - exit /b 1 -) - -:: Clone the repository -echo Cloning XRoboToolkit-PC-Service repository... -git clone https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git -if %errorlevel% neq 0 ( - echo Error: Git clone failed. Exiting. - cd .. - rmdir /s /q %TEMP_DIR% - exit /b 1 -) - -:: Navigate back to the script's root directory to handle destinations -cd %SCRIPT_ROOT% -if %errorlevel% neq 0 ( - echo Error: Failed to navigate back to script root. Exiting. - exit /b 1 -) - -:: --- Copy Header Files --- -echo. -echo Copying header files to %INCLUDE_DEST_DIR%... - -:: Copy PXREARobotSDK.h -set "PXREAROBOTSDK_H_SRC=%PXREAROBOTSDK_SOURCE_DIR%\PXREARobotSDK.h" -echo Copying %PXREAROBOTSDK_H_SRC% -copy "%PXREAROBOTSDK_H_SRC%" "%INCLUDE_DEST_DIR%\" -if %errorlevel% neq 0 ( - echo Error: Failed to copy PXREARobotSDK.h. Exiting. - goto :cleanup_and_exit -) - -:: Create nlohmann subdirectory in include -set "NLOHMANN_INCLUDE_DEST_DIR=%INCLUDE_DEST_DIR%\nlohmann" -echo Ensuring '%NLOHMANN_INCLUDE_DEST_DIR%' directory exists... -mkdir %NLOHMANN_INCLUDE_DEST_DIR% 2>NUL -if %errorlevel% neq 0 ( - echo Error: Failed to create nlohmann include directory. Ignore. -) - -:: Copy nlohmann/json.hpp -set "NLOHMANN_JSON_HPP_SRC=%PXREAROBOTSDK_SOURCE_DIR%\nlohmann\json.hpp" -echo Copying %NLOHMANN_JSON_HPP_SRC% -copy "%NLOHMANN_JSON_HPP_SRC%" "%NLOHMANN_INCLUDE_DEST_DIR%\" -if %errorlevel% neq 0 ( - echo Error: Failed to copy nlohmann/json.hpp. Exiting. - goto :cleanup_and_exit -) - -:: Copy nlohmann/json_fwd.hpp -set "NLOHMANN_JSON_FWD_HPP_SRC=%PXREAROBOTSDK_SOURCE_DIR%\nlohmann\json_fwd.hpp" -echo Copying %NLOHMANN_JSON_FWD_HPP_SRC% -copy "%NLOHMANN_JSON_FWD_HPP_SRC%" "%NLOHMANN_INCLUDE_DEST_DIR%\" -if %errorlevel% neq 0 ( - echo Error: Failed to copy nlohmann/json_fwd.hpp. Exiting. - goto :cleanup_and_exit -) - -echo Header files copied successfully. - -:: --- Copy Pre-built PXREARobotSDK DLL and LIB --- -echo. -echo Checking for pre-built libraries in %PXREAROBOTSDK_LIB_DIR% -set "DLL_SOURCE_PATH=%PXREAROBOTSDK_LIB_DIR%\%DLL_NAME%" -set "LIB_SOURCE_PATH=%PXREAROBOTSDK_LIB_DIR%\%LIB_NAME%" - -if not exist "%DLL_SOURCE_PATH%" ( - echo Error: Required DLL "%DLL_SOURCE_PATH%" not found. - echo Please ensure the cloned repository contains the pre-built files. - goto :cleanup_and_exit -) -if not exist "%LIB_SOURCE_PATH%" ( - echo Error: Required LIB "%LIB_SOURCE_PATH%" not found. - echo Please ensure the cloned repository contains the pre-built files. - goto :cleanup_and_exit -) - -echo Copying %DLL_NAME% to %LIB_DEST_DIR%/ -copy "%DLL_SOURCE_PATH%" "%LIB_DEST_DIR%\" -if %errorlevel% neq 0 ( - echo Error: Failed to copy %DLL_NAME%. Exiting. - goto :cleanup_and_exit -) - -echo Copying %LIB_NAME% to %LIB_DEST_DIR%/ -copy "%LIB_SOURCE_PATH%" "%LIB_DEST_DIR%\" -if %errorlevel% neq 0 ( - echo Error: Failed to copy %LIB_NAME%. Exiting. - goto :cleanup_and_exit -) - -echo Libraries copied successfully. - -:: Build and install the Python project -echo. -echo Building and installing the Python project... -python setup.py install -if %errorlevel% neq 0 ( - echo Error: Python setup.py install failed. Exiting. - goto :cleanup_and_exit -) - -:: Copy DLL to the installed package location -echo. -echo Copying DLL to the installed package location... -for /f "usebackq" %%i in (`python -c "import site; print(site.getsitepackages()[0])"`) do set SITE_PACKAGES=%%i -if not defined SITE_PACKAGES ( - echo Warning: Could not determine site-packages directory. - echo DLL not copied to package location. You may need to do this manually. - goto :cleanup_and_exit -) - -:: Find the egg directory -set "FOUND_EGG=" -for /d %%d in ("%SITE_PACKAGES%\Lib\site-packages\xrobotoolkit_sdk-*") do ( - set "FOUND_EGG=%%d" - goto :egg_found -) -:egg_found - -if not defined FOUND_EGG ( - echo Warning: Could not find xrobotoolkit_sdk egg directory in %SITE_PACKAGES% - echo Looking in easy-install.pth... - if exist "%SITE_PACKAGES%\easy-install.pth" ( - for /f "usebackq tokens=*" %%i in (`findstr /i "xrobotoolkit_sdk" "%SITE_PACKAGES%\easy-install.pth"`) do set "FOUND_EGG=%%i" - ) -) - -if not defined FOUND_EGG ( - echo Warning: Could not find xrobotoolkit_sdk egg directory. - echo DLL not copied to package location. You may need to do this manually. -) else ( - echo Found egg directory: %FOUND_EGG% - echo Copying %DLL_NAME% to %FOUND_EGG% - copy "%LIB_DEST_DIR%\%DLL_NAME%" "%FOUND_EGG%\" - if %errorlevel% neq 0 ( - echo Warning: Failed to copy DLL to egg directory. - ) else ( - echo DLL successfully copied to package location. - ) -) - -echo Setup completed successfully! - -:cleanup_and_exit -:: Remove the temporary directory -echo Cleaning up temporary directory: %TEMP_DIR% -rmdir /s /q "%SCRIPT_ROOT%\%TEMP_DIR%" -if %errorlevel% neq 0 ( - echo Warning: Failed to remove temporary directory "%SCRIPT_ROOT%\%TEMP_DIR%". Please remove it manually. -) - -endlocal \ No newline at end of file diff --git a/src/xrobotoolkit_sdk/__init__.py b/src/xrobotoolkit_sdk/__init__.py new file mode 100644 index 0000000..866ffa0 --- /dev/null +++ b/src/xrobotoolkit_sdk/__init__.py @@ -0,0 +1,23 @@ +"""Python bindings for the XRoboToolkit PC Service SDK. + +Exposes XR device state streamed from a PICO headset via the XRoboToolkit +PC Service: controller poses and inputs, hand tracking, whole-body motion +tracking, and standalone motion trackers. +""" + +from __future__ import annotations + +import os +import sys + + +def _add_windows_dll_directory() -> None: + """Make the bundled PXREARobotSDK.dll resolvable before importing _core.""" + if sys.platform != "win32": + return + os.add_dll_directory(os.path.dirname(os.path.abspath(__file__))) + + +_add_windows_dll_directory() + +from xrobotoolkit_sdk._core import * # noqa: E402,F403 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..33b7d7b --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" + +[[package]] +name = "xrobotoolkit-sdk" +version = "1.1.0" +source = { editable = "." } diff --git a/vendor/README.md b/vendor/README.md new file mode 100644 index 0000000..003c816 --- /dev/null +++ b/vendor/README.md @@ -0,0 +1,17 @@ +# Vendored PXREARobotSDK binaries + +Prebuilt binaries and headers copied verbatim from +[XR-Robotics/XRoboToolkit-PC-Service](https://github.com/XR-Robotics/XRoboToolkit-PC-Service) +at commit `85bac4dbc1fd5cef42c74a160d9c30aa3491f122`. + +| Path | Upstream source | +| --- | --- | +| `include/PXREARobotSDK.h` | `RoboticsService/SDK/include/PXREARobotSDK.h` | +| `include/nlohmann/` | `RoboticsService/PXREARobotSDK/nlohmann/` | +| `linux_x86_64/libPXREARobotSDK.so` | `RoboticsService/SDK/linux/64/` (needs glibc >= 2.29) | +| `linux_aarch64/libPXREARobotSDK.so` | `RoboticsService/SDK/linux_aarch64/64/` (needs glibc >= 2.34) | +| `windows_amd64/PXREARobotSDK.{dll,lib}` | `RoboticsService/SDK/win/64/` | + +To update: clone upstream, copy the files listed above, and record the new +commit hash here. If the glibc requirements of the Linux libraries change, +adjust the `manylinux-*-image` settings in `pyproject.toml` accordingly. diff --git a/include/PXREARobotSDK.h b/vendor/include/PXREARobotSDK.h similarity index 100% rename from include/PXREARobotSDK.h rename to vendor/include/PXREARobotSDK.h diff --git a/include/nlohmann/json.hpp b/vendor/include/nlohmann/json.hpp similarity index 100% rename from include/nlohmann/json.hpp rename to vendor/include/nlohmann/json.hpp diff --git a/include/nlohmann/json_fwd.hpp b/vendor/include/nlohmann/json_fwd.hpp similarity index 100% rename from include/nlohmann/json_fwd.hpp rename to vendor/include/nlohmann/json_fwd.hpp diff --git a/vendor/linux_aarch64/libPXREARobotSDK.so b/vendor/linux_aarch64/libPXREARobotSDK.so new file mode 100644 index 0000000..bad131e Binary files /dev/null and b/vendor/linux_aarch64/libPXREARobotSDK.so differ diff --git a/vendor/linux_x86_64/libPXREARobotSDK.so b/vendor/linux_x86_64/libPXREARobotSDK.so new file mode 100644 index 0000000..2d651c2 Binary files /dev/null and b/vendor/linux_x86_64/libPXREARobotSDK.so differ diff --git a/vendor/windows_amd64/PXREARobotSDK.dll b/vendor/windows_amd64/PXREARobotSDK.dll new file mode 100644 index 0000000..519ac70 Binary files /dev/null and b/vendor/windows_amd64/PXREARobotSDK.dll differ diff --git a/vendor/windows_amd64/PXREARobotSDK.lib b/vendor/windows_amd64/PXREARobotSDK.lib new file mode 100644 index 0000000..edf6282 Binary files /dev/null and b/vendor/windows_amd64/PXREARobotSDK.lib differ