Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions build_libicsneo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,28 @@
LIBUSB_BUILD = f"{LIBUSB_ROOT}/build"
LIBUSB_INSTALL = f"{LIBUSB_ROOT}/install"

# NOTE: this pin's vendored icsnVC40.h predates Vspy 3.26.3.9
# (SRADGalaxy2Settings 840 vs 1204, no Comet3/Gigastar2), so Linux/macOS
# wheels lag the Windows ABI. Newer upstream doesn't help yet: master
# (4ed29b4, 2026-07-07) still has the old header AND adds a mandatory
# icspb protobuf dependency whose codegen fails on clean CI runners.
# Bump once upstream syncs the header and icspb builds.
LIBICSNEO_VERSION = "830fe1a"
# libicsneo master 2026-07-08; its vendored icsnVC40.h is byte-identical
# to include/ics/icsnVC40.h (Vspy 3.26.3.9), so Linux/macOS wheels match
# the Windows ABI. Keep the two headers in lockstep on future bumps.
# libicsneo requires icspb (protobuf) as of 2026: its codegen needs the
# Ninja generator (protobuf_generate never creates PROTOC_OUT_DIR; Make
# doesn't pre-create declared output dirs, Ninja does), and it bootstraps
# protobuf from source at configure time — see ICSPB_BOOTSTRAP_DIR below.
LIBICSNEO_VERSION = "0dd8dbf"
LIBICSNEO_ROOT = f"{ROOT}/libicsneo/{LIBICSNEO_VERSION}"
LIBICSNEO_SOURCE = f"{LIBICSNEO_ROOT}/source"
LIBICSNEO_BUILD = f"{LIBICSNEO_ROOT}/build"
LIBICSNEO_INSTALL = f"{LIBICSNEO_ROOT}/install"
print(f"LIBICSNEO PATH: {LIBICSNEO_ROOT}")

# icspb bootstraps protobuf from source at configure time. Keep the
# bootstrap OUTSIDE the libicsneo build dir so it survives the per-python
# `git clean` in _build_libicsneo_linux and is reused across all
# cibuildwheel builds in a job (it self-partitions by <system>-<processor>,
# so sharing one root across archs is safe). Building protobuf once per
# arch instead of once per python matters most under QEMU aarch64.
ICSPB_BOOTSTRAP_DIR = f"{ROOT}/icspb_bootstrap"

LIBPCAP_VERSION = "1.10.4"
LIBPCAP_ROOT = f"{ROOT}/libpcap/{LIBPCAP_VERSION}"
LIBPCAP_SOURCE = f"{LIBPCAP_ROOT}/source"
Expand Down Expand Up @@ -102,6 +111,18 @@ def _build_libpcap():
subprocess.check_output(["make", "-j" + CPUS], cwd=LIBPCAP_BUILD)
subprocess.check_output(["make", "install"], cwd=LIBPCAP_BUILD)

def _cmake_ninja_args():
# pip's ninja lives inside each isolated build env, and cibuildwheel
# deletes that env between python versions. A reused CMake build dir
# caches the old (now dead) path in CMAKE_MAKE_PROGRAM and fails at
# the next configure, so always override with the current ninja.
ninja = shutil.which("ninja")
args = ["-G", "Ninja"]
if ninja:
args.append(f"-DCMAKE_MAKE_PROGRAM={ninja}")
return args


def _build_libicsneo_linux():
print("Cleaning libicsneo...")
subprocess.check_output(["git", "clean", "-xdf"], cwd="libicsneo")
Expand All @@ -111,9 +132,13 @@ def _build_libicsneo_linux():
subprocess.check_output(
[
"cmake",
# Ninja is required: icspb's protobuf_generate never creates its
# output dir, and only Ninja pre-creates declared output dirs.
*_cmake_ninja_args(),
"-DCMAKE_BUILD_TYPE=Release",
"-DLIBICSNEO_BUILD_ICSNEOLEGACY=ON",
f"-DCMAKE_PREFIX_PATH={LIBUSB_INSTALL};{LIBPCAP_INSTALL}",
f"-DICSPB_BOOTSTRAP_DIR={ICSPB_BOOTSTRAP_DIR}",
"-S", LIBICSNEO_SOURCE,
"-B", LIBICSNEO_BUILD,
"-Wno-dev",
Expand All @@ -125,17 +150,29 @@ def _build_libicsneo_linux():
)

def _build_libicsneo_macos():
env = os.environ.copy()
# Set as env vars (not only -D flags) so icspb's nested protobuf
# bootstrap — a separate cmake invocation via execute_process, which
# inherits the environment but not our -D cache entries — also builds
# universal2 static libs instead of arm64-only ones.
env["CMAKE_OSX_ARCHITECTURES"] = "arm64;x86_64"
env["CMAKE_OSX_DEPLOYMENT_TARGET"] = "10.13"
subprocess.check_output(
[
"cmake",
# Ninja is required: icspb's protobuf_generate never creates its
# output dir, and only Ninja pre-creates declared output dirs.
*_cmake_ninja_args(),
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64",
"-DLIBICSNEO_BUILD_ICSNEOLEGACY=ON",
"-DCMAKE_OSX_DEPLOYMENT_TARGET=10.13",
f"-DCMAKE_PREFIX_PATH={LIBUSB_INSTALL};{LIBPCAP_INSTALL}",
f"-DICSPB_BOOTSTRAP_DIR={ICSPB_BOOTSTRAP_DIR}",
"-S", LIBICSNEO_SOURCE,
"-B", LIBICSNEO_BUILD
]
],
env=env,
)

subprocess.check_output(
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ requires = [
"wheel",
"dunamai",
"pytest>=8.4.2",
# Puts the ninja executable on the isolated build env's PATH; required by
# build_libicsneo.py's `cmake -G Ninja` on Linux/macOS (see comments there).
"ninja",
]


Expand Down
Loading