Skip to content

fix(PythonAPI/build): add NumPy 2 compatibility via Boost 1.90 upgrade - #9670

Merged
LuisPovedaCano merged 5 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:fix/numpy2_compatibility
Apr 17, 2026
Merged

fix(PythonAPI/build): add NumPy 2 compatibility via Boost 1.90 upgrade #9670
LuisPovedaCano merged 5 commits into
carla-simulator:ue4-devfrom
JArmandoAnaya:fix/numpy2_compatibility

Conversation

@JArmandoAnaya

@JArmandoAnaya JArmandoAnaya commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Description

Upgrades Boost from 1.84.0 to 1.90.0 and, as a direct consequence, makes CARLA's
PythonAPI compatible with both NumPy 1.x (>= 1.18.4) and NumPy 2.x. NumPy 1.x
support is preserved, not dropped.

We first landed the upgrade at Boost 1.89.0, which is the minimum version that
ships the upstream NumPy 2 C ABI fix
(boostorg/python#432) natively.
Moving from 1.89.0 to 1.90.0 was a small follow-up: 1.90 only added
[[deprecated]] annotations to boost::asio::deadline_timer, which we migrated
to boost::asio::steady_timer. Going straight to the newest stable release
avoids carrying a known-deprecated API into the tree.


Improvements

  • Boost upgraded from 1.84.0 -> 1.90.0 on Linux and Windows. Old cached
    boost-1.84.0-install/ trees are picked up automatically via the updated
    BOOST_VERSION and SHA256 pins in Util/BuildTools/Setup.sh,
    Util/BuildTools/Setup.bat, Util/BuildTools/BuildOSMRenderer.bat, and
    Util/InstallersWin/install_boost.bat.
  • LibCarla networking migrated from boost::asio::deadline_timer to
    boost::asio::steady_timer in Primary, Secondary, Client, and
    ServerSession. No deprecated Asio API remains in the tree.
  • Python requirements widened from numpy<2.0.0 to numpy>=1.18.4 in
    PythonAPI/examples/requirements.txt, PythonAPI/carla/agents/requirements.txt,
    and PythonAPI/test/requirements.txt.
  • Docker CI test requirements (Util/Docker/requirements/3.11/test.txt and
    3.12/test.txt) pinned to numpy>=2.0.0 to exercise NumPy 2.x on CI.
  • Vendored patch Util/Patches/boost-1.84.0/dtype.cpp.patch and its helpers
    (verify_patch.cpp, verify_patch.sh) are deleted. Boost ships the fix now.

Fixes

  • NumPy 2 Python-level removals. np.bool replaced with np.bool_ in
    PythonAPI/examples/V2XDemo.py, manual_control_chrono.py, and
    manual_control_carsim.py (DVS event dtype). np.matrix(np.identity(4))
    replaced with np.array(np.identity(4)) in client_bounding_boxes.py, safe
    because the only consumer is np.dot().

  • NumPy 2 C ABI (NEP 52).
    Boost 1.89+ makes libboost_numpy build against both NumPy 1.x and 2.x
    headers without any vendored patches (PyArray_Descr is now opaque).

  • Boost 1.84 -> 1.90 API call-site updates in LibCarla and the CarlaTools
    UE4 plugin (each removal was introduced between 1.85 and 1.90):

File Change
LibCarla/source/carla/ThreadPool.h io_context::work -> executor_work_guard
LibCarla/source/carla/multigpu/listener.cpp io_context::reset() -> restart()
LibCarla/source/carla/multigpu/router.cpp, secondary.cpp address::from_string() -> make_address()
LibCarla/source/carla/rpc/Server.h reset() -> restart()
LibCarla/source/carla/streaming/EndPoint.h resolver::query/iterator -> range-based resolve()
LibCarla/source/carla/FileSystem.cpp added #include <boost/filesystem/directory.hpp> (split in 1.85)
Unreal/.../MapPreviewUserWidget.{h,cpp} io_service -> io_context
buffer_cast<T>() -> static_cast via streambuf::data().data()
MSVC C4459 pragma guard
  • steady_timer default-construction trap. deadline_timer defaulted to
    pos_infin, but steady_timer default-constructs with expiry() == time_point::min()
    (in the past). Primary::StartTimer and ServerSession::StartTimer guard on
    expiry() <= now() to detect timeouts, so the default would fire Close()
    before the socket is set up. _deadline is now explicitly initialised with
    std::chrono::steady_clock::time_point::max() in both constructors.

  • Call-site API migrations for the timer rewrite:
    expires_from_now(d) -> expires_after(d), expires_at() -> expiry(),
    deadline_timer::traits_type::now() -> std::chrono::steady_clock::now(),
    boost::posix_time durations -> std::chrono durations.


Added testing

New regression guards added under LibCarla/source/test/common/ and
PythonAPI/test/unit/:

File Coverage
test_boost_deadline_timer.cpp
(test class BoostAsioTimer)
6 tests: TimerFiresWithinReasonableTime, ExpiresAtNowComparison, ChronoDurationComposition,
InitializedWithMaxIsInDistantFuture (pins the time_point::max() fix),
DefaultConstructedExpiryIsInPast (documents the trap),
CancelTriggersAbortedError (covers the CloseNow() path)
test_boost_rtree.cpp 4 tests guarding geometry::index::rtree (used by TrafficManager)
test_boost_version.py Asserts the Boost version pinned in Setup.sh is current (1.90.0) and no stale boost-1.84.0 patch dir or link exists
test_numpy_compat.py 15 tests: Python alias regressions, DVS pipeline, carla C extension load under NumPy 1.x and 2.x
run_numpy_compat_matrix.sh Matrix runner: creates venvs for NumPy 1.x and 2.x, installs the wheel, runs test_numpy_compat.py in each

Commits

  1. d4151ed05 fix(PythonAPI/examples): replace deprecated NumPy aliases
  2. 69df4d2a1 fix(build): upgrade Boost to 1.89.0 for NumPy 2 ABI compatibility
  3. a5e4ce0d3 docs(CHANGELOG): note NumPy 2 compatibility
  4. 5e68bef2c fix(build): bump Boost 1.89 -> 1.90 and migrate timers to steady_timer

Fixes #9365


Where has this been tested?

  • Platform(s): Ubuntu 22.04
  • Python version(s): 3.8, 3.9, 3.10, 3.11, 3.12
  • Unreal Engine version(s): UE4.26

Results:

  • make check.LibCarla passes in both debug and release on the server and
    client suites, including all 6 BoostAsioTimer.* and 4 BoostRtree.* tests.
  • make check.PythonAPI passes, including the full test_boost_version.py
    set (static Boost version pin + no stale 1.84.0 link).
  • python3 -m nose2 -v --start-dir PythonAPI/test/unit test_numpy_compat on
    Python 3.12 + NumPy 2.4.4 with the carla wheel installed: 15/15 pass.
  • bash PythonAPI/test/unit/run_numpy_compat_matrix.sh python3.12:
    NumPy 1.26.4 venv 15/15 pass, NumPy 2.4.4 venv 15/15 pass. The 2.x log
    confirms end-to-end load:
    carla C extension OK with NumPy 2.4.4, NumPy 2.x (NEP 52, opaque PyArray_Descr).
  • CI-equivalent package build: make package ARGS="--python-version=3.10,3.11,3.12 --ros2 --chrono --no-zip --target-wheel-platform=manylinux_2_31_x86_64": SUCCESS.
  • Smoke tests (Python 3.12, port 3654): all pass.

Possible Drawbacks

  • Users with a cached Build/boost-1.84.0-c10-install/ or
    Build/boost-1.84.0-install/ tree from a previous checkout need to remove it
    before building, so the 1.90.0 source is fetched and compiled. make clean
    is sufficient.
  • Windows: install_boost.bat SHA256 has been updated to the 1.90.0 source-zip
    digest. Cached boost-1.84.0-install/ directories on Windows build agents
    must likewise be removed. The Windows CI path does not build ROS2, so none of
    the Asio/Filesystem call-site changes affect the Windows CI build.

This change is Reviewable

@JArmandoAnaya
JArmandoAnaya requested a review from a team as a code owner April 14, 2026 06:54
@LuisPovedaCano LuisPovedaCano self-assigned this Apr 14, 2026
@Blyron

Blyron commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Why should we merge this?

@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Fair enough. I expect NumPy 1.x to reach end of life, and downstream libraries are already moving to require NumPy 2 (e.g., opencv-python >= 4.12). Having this compatibility in place before it becomes urgent seems better than scrambling later, but I understand if the priority isn't there yet.

@Blyron

Blyron commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

What about using updated version of boost instead of patching it? I do not like patches.

@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Good point. I went with a patch to avoid a full Boost version bump, which could be quite invasive in this refactoring, since we only needed to change a few things from Boost. But if you’d prefer to upgrade Boost instead, I can look into it and send a new commit.

@Blyron

Blyron commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Look at this https://github.com/carla-simulator/carla/pull/9487/changes#diff-e69ecb8d585c244655338ab3d5b078f10bca6c28d00bf4357e064baf193d0e45

is a PR from a former CARLA collaborator.

@JArmandoAnaya
JArmandoAnaya marked this pull request as draft April 14, 2026 17:15
@JArmandoAnaya
JArmandoAnaya force-pushed the fix/numpy2_compatibility branch from 8aa6489 to b3f4d55 Compare April 14, 2026 20:27
@JArmandoAnaya JArmandoAnaya changed the title fix(PythonAPI): NumPy 2 compatibility and Boost.Python ABI patch fix(PythonAPI/build): add NumPy 2 compatibility via Boost 1.89 upgrade Apr 14, 2026
@JArmandoAnaya JArmandoAnaya changed the title fix(PythonAPI/build): add NumPy 2 compatibility via Boost 1.89 upgrade fix(PythonAPI/build): add NumPy 2 compatibility via Boost 1.90 upgrade Apr 16, 2026
… compatibility

Replace np.bool with np.bool_ in DVS event dtype in V2XDemo.py,
manual_control_chrono.py, and manual_control_carsim.py (removed in
NumPy 1.24). Replace np.matrix with np.array in client_bounding_boxes.py
(deprecated in NumPy 1.x, removed in NumPy 2.x). All replacements are
backward-compatible with NumPy >= 1.18.4.
Drop the in-tree dtype.cpp.patch against Boost 1.84.0 and upgrade to
Boost 1.89.0, which carries the upstream NumPy 2 C ABI fix from
boostorg/python#432 natively. libboost_numpy now compiles against both
NumPy 1.x (>=1.18.4) and NumPy 2.x headers without any vendored patches.

Boost 1.84 -> 1.89 removed several deprecated Asio and Filesystem APIs;
all call sites in LibCarla and the CarlaTools UE4 plugin updated:
- io_context::work -> executor_work_guard (removed 1.85)
- io_context::reset() -> restart() (removed 1.86)
- address::from_string() -> make_address() (removed 1.85)
- resolver::query/iterator -> range-based resolve() (removed 1.85)
- buffer_cast<T>() -> static_cast via streambuf::data().data() (removed 1.88)
- io_service typedef -> io_context (removed 1.85)
- boost/filesystem/operations.hpp no longer includes directory.hpp (1.85)

Windows installer (install_boost.bat) SHA256 updated to the 1.89.0 zip
digest. MSVC C4459 pragma guard added around boost/asio.hpp in
MapPreviewUserWidget.h to prevent warnings-as-errors failure.

New regression-guard tests: test_boost_deadline_timer.cpp (3 tests),
test_boost_rtree.cpp (4 tests), test_boost_version.py.
Boost 1.90 added [[deprecated]] annotations to boost::asio::deadline_timer.
LibCarla's networking code is migrated to boost::asio::steady_timer across
Primary, Secondary, Client, and ServerSession.

steady_timer default-constructs with expiry() == time_point::min() (past),
whereas deadline_timer defaulted to pos_infin (far future). The recursive
StartTimer() guard in ServerSession::StartTimer and Primary::StartTimer
checks expiry() <= now() and would fire Close() before socket setup if left
at the new default, so both _deadline members are explicitly constructed
with time_point::max() to preserve the old semantics.

API migrations at the call sites:
  * expires_from_now(duration)          -> expires_after(duration)
  * expires_at()                        -> expiry()
  * deadline_timer::traits_type::now()  -> std::chrono::steady_clock::now()
  * boost::posix_time durations         -> std::chrono durations

Build scripts:
  * Util/BuildTools/Setup.sh:      BOOST_VERSION 1.89.0 -> 1.90.0, SHA256
  * Util/BuildTools/Setup.bat:     version bump (Windows)
  * Util/BuildTools/BuildOSMRenderer.bat: install path bump (Windows)
  * Util/InstallersWin/install_boost.bat: source-zip SHA256 bump

Tests:
  * test_boost_deadline_timer.cpp: test class BoostDeadlineTimer renamed
    to BoostAsioTimer and migrated to steady_timer; 3 new regression
    tests pin the contract that prevents future refactors from silently
    reintroducing the default-construction trap:
      - InitializedWithMaxIsInDistantFuture
      - DefaultConstructedExpiryIsInPast
      - CancelTriggersAbortedError
  * test_boost_rtree.cpp:          header comment 1.89 -> 1.90
  * test_boost_version.py:         EXPECTED_BOOST_VERSION 1.89.0 -> 1.90.0;
                                   stale-cache check rolled 1.84.0 -> 1.89.0
  * CHANGELOG.md:                  NumPy 2 entry references Boost 1.90.0

Verified: make LibCarla + full make check.LibCarla pass on server and
client, release and debug. All 6 BoostAsioTimer.* and 4 BoostRtree.* tests
pass on both suites.
@JArmandoAnaya
JArmandoAnaya force-pushed the fix/numpy2_compatibility branch from 7ff59bc to 5e68bef Compare April 16, 2026 20:41
@JArmandoAnaya
JArmandoAnaya marked this pull request as ready for review April 17, 2026 06:28
@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Ready! I upgraded Boost to 1.90. I used the example from @MarcelPiNacy-CVC as the first try. But, after that, migrating from 1.89 to 1.90 required only a few extra steps, so I did it in a second commit.

@LuisPovedaCano LuisPovedaCano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this PR!
The API migrations are all correct, the time_point::max() initialization fix is a subtle but important catch, and the test coverage is thorough.
Thanks for the effort put into this!

One small observation: test_no_boost_189_in_linked_libs in test_boost_version.py guards against accidentally linking a stale boost-1.89.0 install (the intermediate version used during development of this PR), but a user upgrading directly from the previous 1.84.0 baseline would slip past it undetected.
Since that's arguably the more common stale-cache scenario for most contributors, it might be worth adding a similar check for boost-1.84.0 there too or widening the assertion to something like "must link against boost-1.90.0" rather than "must not link against boost-1.89.0"

Address review on PR carla-simulator#9670. The previous test guarded only against the 1.89.0 intermediate install used during development, letting a direct
  1.84.0 -> 1.90.0 upgrade slips past undetected.

- Replace the negative-only LDD check with a positive assertion that libboost_*.so.1.90.0 is present in PythonAPI/carla/dependencies/lib/, plus an explicit guard over known stale baselines (1.84.0, 1.89.0). Boost is statically linked into libcarla.so, so ldd cannot observe it; the dependencies/lib symlinks are the authoritative record.
- Update two Linux build scripts still pinned to boost-1.84.0 that were missed in the 1.84->1.89->1.90 bumps (the Windows .bat was updated):
  - Util/BuildTools/BuildOSMRenderer.sh
  - Util/BuildTools/Ad-rss.sh
@JArmandoAnaya

Copy link
Copy Markdown
Contributor Author

Hello! Thanks for the code review. I made the update.

@LuisPovedaCano LuisPovedaCano left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@LuisPovedaCano
LuisPovedaCano merged commit 6e15235 into carla-simulator:ue4-dev Apr 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants