Skip to content

Conversation

@Lmh-java
Copy link
Contributor

@Lmh-java Lmh-java commented Sep 21, 2025

closes #3529

Description

This PR introduces native macOS ARM support for Thunderscope.

Currently, this PR is experimental and is just providing a minimal working demo on my macbook M1.

Out of scope for this PR:

  1. Cross-compilation and robot hardware interactions
  2. Support for Intel (x86) MacBooks

Testing Done

  • Run extensive tests on additional macOS ARM machines. My current laptop includes many pre-installed developer tools, so the setup script may need updates to ensure a clean installation experience.
  • Verify functionality on Ubuntu machines to confirm no regressions were introduced.

TODO List

For this PR:

  • Fix the broken local_new_repositories and confirm compatibility with Ubuntu
  • Separate linux and macos requirements lock files, because evdev does not exist for macos.

Maybe for future PRs or possibly this PR:

  • If necessary, re-implement handheld controller widgets using a macOS-compatible library
  • Deduplicate TbotsProto.ObstacleList to prevent system network buffer overload (TrajectoryPlanner improvements #3104). This is impacting the stability of thunderscope on macOS. I am currently working on a sliding window cache that hopefully can mitigate this issue.
  • Fix unit tests

@Lmh-java Lmh-java force-pushed the lmh/macos-support-new branch 2 times, most recently from 3ca31a1 to c308ffb Compare October 18, 2025 18:51
@Lmh-java Lmh-java changed the title [DRAFT] Add macOS ARM support for Thunderscope Add macOS ARM support for Thunderscope Oct 18, 2025
Comment on lines 93 to 103
install_java_macos () {
java_home=""
java_download=https://download.oracle.com/java/21/latest/jdk-21_macos-aarch64_bin.tar.gz
curl -L $java_download -o /tmp/tbots_download_cache/jdk-21.tar.gz
mkdir /tmp/tbots_download_cache/jdk
tar -xzf /tmp/tbots_download_cache/jdk-21.tar.gz -C /tmp/tbots_download_cache
sudo mv /tmp/tbots_download_cache/jdk-21*/Contents/Home /opt/tbotspython/bin/jdk
rm /tmp/tbots_download_cache/jdk-21.tar.gz
rm -rf /tmp/tbots_download_cache/jdk
}

Copy link

Choose a reason for hiding this comment

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

Java Installed already from brew, I don't think we need this

Copy link
Contributor

Choose a reason for hiding this comment

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

You need a specific version of Java for autoref (Java 21)

Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose you're using the autoref binary package, so you can probably ignore this

Copy link
Contributor

@itsarune itsarune left a comment

Choose a reason for hiding this comment

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

Left a few comments

Comment on lines 93 to 103
install_java_macos () {
java_home=""
java_download=https://download.oracle.com/java/21/latest/jdk-21_macos-aarch64_bin.tar.gz
curl -L $java_download -o /tmp/tbots_download_cache/jdk-21.tar.gz
mkdir /tmp/tbots_download_cache/jdk
tar -xzf /tmp/tbots_download_cache/jdk-21.tar.gz -C /tmp/tbots_download_cache
sudo mv /tmp/tbots_download_cache/jdk-21*/Contents/Home /opt/tbotspython/bin/jdk
rm /tmp/tbots_download_cache/jdk-21.tar.gz
rm -rf /tmp/tbots_download_cache/jdk
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I suppose you're using the autoref binary package, so you can probably ignore this

Copy link
Contributor

Choose a reason for hiding this comment

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

You should make sure you're using the same c++ compuler version as Ubuntu (to minimize the number of c++ compatibility changes that are needed)

requirement("evdev"),
],
] + select({
# TODO: remove this selection when we replace evdev to
Copy link
Contributor

Choose a reason for hiding this comment

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

Tag an issue with the TODO

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that was the first thing we have tried, but it breaks the dependency tree because other targets depending one this are all marked incompatible. I will do more test on this.

@Lmh-java Lmh-java force-pushed the lmh/macos-support-new branch from c308ffb to d6b303d Compare November 15, 2025 19:28

import evdev
from evdev import ecodes
# TODO: remove the try-catch when we rewrite this with macOS-compatible lib
Copy link
Contributor

Choose a reason for hiding this comment

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

src/.bazelrc Outdated
# Escalate Warnings to fail Compile for Thunderbots code
build --features=external_include_paths
build --per_file_copt=proto/.*,proto/message_translation/.*,proto/primitive/.*,software/.*,shared/.*,-external/.*@-Wall,-Wextra,-Wno-unused-parameter,-Wno-deprecated,-Werror,-Wno-deprecated-declarations
build:linux --features=external_include_paths
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally these flags would also work on macos

Copy link
Contributor Author

@Lmh-java Lmh-java Nov 22, 2025

Choose a reason for hiding this comment

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

Yep, ideally it should also work on macos, but because the macos clang compiler is stricter than linux and reports millions of new errors (converted from warnings). :(

Copy link
Contributor

@itsarune itsarune left a comment

Choose a reason for hiding this comment

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

Left some comments

@Andrewyx
Copy link
Contributor

Left some comments

I think we are getting close w/ getting this working, I put in a PR last week to hopefully reconcile some bazel hermiticity issues and we'll run a test this week to check if it actually works on anyone else's machines aside from minghao's.

@nycrat
Copy link
Member

nycrat commented Dec 3, 2025

Just a note, I did get this working on my m1 macbook as well. I did however have to resolve some c++ compatibility issues, and also update the package required for bazel refresh_compile_commands to resolve a bug. I'm not sure if these issues were also present on your macbook?

We should definitely test some time with a clean install, and to test all scripts and commands that we want to support for mac to make sure they also work.

@Lmh-java Lmh-java marked this pull request as ready for review December 6, 2025 19:06
@Lmh-java Lmh-java requested a review from Andrewyx as a code owner December 6, 2025 19:06
@Lmh-java
Copy link
Contributor Author

Lmh-java commented Dec 6, 2025

@nycrat
Yes, we have seen this resolve some c++ compatibility issues on Annie's laptop as well, and I have the same fix.

Personally, I dont use the refresh_compile_commands. That's a great catch. I think it is less likely to affect ubuntu users by just updating the version. I will cherry-pick your commit onto this.

@nycrat
Copy link
Member

nycrat commented Jan 9, 2026

There are still a few issues on my end when testing from this branch:

Firstly, the cpp tests don't seem to work, because tbots_gtest_main.cpp uses the feenableexcept and fedisableexcept functions which are apparently not available on MacOS. Definitely will have to refactor both tbots_gtest_main.cpp and simulated_er_force_sim_test_fixture.cpp since these functions are used.

image

Also, I get these error messages from the ThreadSafeBuffer class, though I'm not sure the exact reason and I'm not that familiar with multithreaded c++

image image

@Lmh-java
Copy link
Contributor Author

Lmh-java commented Jan 9, 2026

Firstly, the cpp tests don't seem to work, because tbots_gtest_main.cpp uses the feenableexcept and fedisableexcept functions which are apparently not available on MacOS. Definitely will have to refactor both tbots_gtest_main.cpp and simulated_er_force_sim_test_fixture.cpp since these functions are used.

Interesting. Thanks for reminding me that. I intentionally did not run any of the unit tests, because I'd anticipate more failures than just 'feenableexcept' and 'fedisableexcept' (they have to be fixed by polyfilling, such as https://gitlab.dkrz.de/dkrz-sw/yac/-/blob/v3.9.1/src/core/feenableexcept.h). I definitely agree it will be great to have the unit tests running locally, but I wanted to scope this PR to just get Thunderscope UI running, so some of the UI visualization work can be running on mac. Currently, we have CI that checks the unit tests from each PR, so this should not be very urgent. I will work on unit tests in the upcoming PRs.

Also, I get these error messages from the ThreadSafeBuffer class, though I'm not sure the exact reason and I'm not that familiar with multithreaded c++

This is an another problem related to our network traffic in general (not just macos).
We are sending lots of duplicated packets over the network, which overwhelms the system buffer. Tracked at #3549.

This is more obvious on mac because there is a subtle difference between how macos and ubuntu implement the network interface. When the ubuntu system buffer is full, the system call will block. Whereas, on macos, it throws an error.

Copy link
Member

@nycrat nycrat left a comment

Choose a reason for hiding this comment

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

Added some comments mainly about the setup script

Copy link
Contributor

@GrayHoang GrayHoang left a comment

Choose a reason for hiding this comment

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

Will there be any issues with the new bazel_rules stuff again?

@@ -0,0 +1,13 @@
ansible-lint==24.12.2
pyqtgraph==0.13.7
Copy link
Contributor

Choose a reason for hiding this comment

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

A lot of these can be removed pending #3542 I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will do a merge test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Uhh I think bazel has no problem with your PR, everything compiles. But PyQt6 conflicts still exist with the current commit 9fce33a1727218c76ee0e1b37ee26bbbd1d0a1e1. I would guess this issue is normal because you have not resolved this PyQt lib conflict in your branch yet?

Full Error message:

INFO: Running command line: bazel-bin/software/thunderscope/thunderscope_main
  ⚠ Conflicting symlinks found when attempting to create venv. More than one package provides the file at these paths
   ╭─[1:208]
 1 │ /private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/.thunderscope_main.venv/lib/python3.12/site-packages/PyQt6/__init__.py
   ·                                                                                                                                                                                                                ───────────────┬───────────────
   ·                                                                                                                                                                                                                               ╰── Existing file in virtual environment
 2 │ /private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/rules_python++pip+thunderscope_deps_312_pyqt6/site-packages/PyQt6/__init__.py
   ·                                                                                                                                                                                                                       ───────────────┬───────────────
   ·                                                                                                                                                                                                                                      ╰── Next file to link
   ╰────
  help: Set `package_collisions = "ignore"` on the binary or test rule to ignore this warning

Traceback (most recent call last):
  File "/private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/_main/software/thunderscope/thunderscope_main.py", line 18, in <module>
    from software.thunderscope.thunderscope import Thunderscope
  File "/private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/_main/software/thunderscope/thunderscope.py", line 7, in <module>
    import pyqtgraph
  File "/private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/.thunderscope_main.venv/lib/python3.12/site-packages/pyqtgraph/__init__.py", line 18, in <module>
    from .colors import palette
  File "/private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/.thunderscope_main.venv/lib/python3.12/site-packages/pyqtgraph/colors/palette.py", line 1, in <module>
    from ..Qt import QtGui
  File "/private/var/tmp/_bazel_liminghao/32d7fe8baee71625f01e1bffab4e0a4a/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/software/thunderscope/thunderscope_main.runfiles/.thunderscope_main.venv/lib/python3.12/site-packages/pyqtgraph/Qt/__init__.py", line 57, in <module>
    raise ImportError("PyQtGraph requires one of PyQt5, PyQt6, PySide2 or PySide6; none of these packages could be imported.")
ImportError: PyQtGraph requires one of PyQt5, PyQt6, PySide2 or PySide6; none of these packages could be imported.

@maggiettu
Copy link

not working :(

/opt/tbotspython/lib/python3.12/site-packages/iterfzf/init.py:8: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
from pkg_resources import resource_exists, resource_filename
no actions running
Loading: 0 packages loaded
Loading: 0 packages loaded
Loading: 84 packages loaded
currently loading: software/ai/hl/stp/tactic/goalie ... (35 packages)
Loading: 84 packages loaded
currently loading: software/ai/hl/stp/tactic/goalie ... (35 packages)
Loading: 84 packages loaded
currently loading: software/ai/hl/stp/tactic/goalie ... (35 packages)
Loading: 0 packages loaded
Found target //software/thunderscope:thunderscope_main with confidence 95
bazel run //software/thunderscope:thunderscope_main --copt=-O3 -- --enable_autoref
INFO: Invocation ID: 83800c4c-9552-414f-9419-f7eb8441b3d5
INFO: Analyzed target //software/thunderscope:thunderscope_main (288 packages loaded, 29301 targets configured).
ERROR: /private/var/tmp/_bazel_maggie/fcbdf09fb1f4bb83d4410fd4f9935ff6/external/abseil-cpp+/absl/log/internal/BUILD.bazel:312:11: Compiling absl/log/internal/structured_proto.cc [for tool] failed: (Exit 1): cc_wrapper.sh failed: error executing CppCompile command (from target @@abseil-cpp+//absl/log/internal:structured_proto) external/rules_cc++cc_configure_extension+local_config_cc/cc_wrapper.sh -U_FORTIFY_SOURCE -fstack-protector -Wall -Wthread-safety -Wself-assign -Wunused-but-set-parameter -Wno-free-nonheap-object ... (remaining 86 arguments skipped)

Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
In file included from external/abseil-cpp+/absl/log/internal/structured_proto.cc:16:
external/abseil-cpp+/absl/log/internal/structured_proto.h:91:16: error: 'visit<BufferSizeVisitor, std::variant<std::variant<unsigned long long, long long, unsigned int, int, bool>, std::variant<unsigned long long, long long, double>, absl::Span, std::variant<unsigned int, int, float>> &, void>' is unavailable: introduced in macOS 10.13
return absl::visit(BufferSizeVisitor{field.field_number}, field.value);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1710:20: note: 'visit<BufferSizeVisitor, std::variant<std::variant<unsigned long long, long long, unsigned int, int, bool>, std::variant<unsigned long long, long long, double>, absl::Span, std::variant<unsigned int, int, float>> &, void>' has been explicitly marked unavailable here
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
^
external/abseil-cpp+/absl/log/internal/structured_proto.cc:84:18: error: 'visit<absl::log_internal::(anonymous namespace)::VarintEncoderVisitor, std::variant<unsigned long long, long long, unsigned int, int, bool> &, void>' is unavailable: introduced in macOS 10.13
return absl::visit(VarintEncoderVisitor{field_number, buf}, varint);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1710:20: note: 'visit<absl::log_internal::(anonymous namespace)::VarintEncoderVisitor, std::variant<unsigned long long, long long, unsigned int, int, bool> &, void>' has been explicitly marked unavailable here
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
^
external/abseil-cpp+/absl/log/internal/structured_proto.cc:88:18: error: 'visit<absl::log_internal::(anonymous namespace)::I64EncoderVisitor, std::variant<unsigned long long, long long, double> &, void>' is unavailable: introduced in macOS 10.13
return absl::visit(I64EncoderVisitor{field_number, buf}, i64);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1710:20: note: 'visit<absl::log_internal::(anonymous namespace)::I64EncoderVisitor, std::variant<unsigned long long, long long, double> &, void>' has been explicitly marked unavailable here
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
^
external/abseil-cpp+/absl/log/internal/structured_proto.cc:98:18: error: 'visit<absl::log_internal::(anonymous namespace)::I32EncoderVisitor, std::variant<unsigned int, int, float> &, void>' is unavailable: introduced in macOS 10.13
return absl::visit(I32EncoderVisitor{field_number, buf}, i32);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1710:20: note: 'visit<absl::log_internal::(anonymous namespace)::I32EncoderVisitor, std::variant<unsigned int, int, float> &, void>' has been explicitly marked unavailable here
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
^
external/abseil-cpp+/absl/log/internal/structured_proto.cc:109:16: error: 'visit<absl::log_internal::(anonymous namespace)::EncoderVisitor, std::variant<std::variant<unsigned long long, long long, unsigned int, int, bool>, std::variant<unsigned long long, long long, double>, absl::Span, std::variant<unsigned int, int, float>> &, void>' is unavailable: introduced in macOS 10.13
return absl::visit(EncoderVisitor{field.field_number, buf}, field.value);
^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/variant:1710:20: note: 'visit<absl::log_internal::(anonymous namespace)::EncoderVisitor, std::variant<std::variant<unsigned long long, long long, unsigned int, int, bool>, std::variant<unsigned long long, long long, double>, absl::Span, std::variant<unsigned int, int, float>> &, void>' has been explicitly marked unavailable here
decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
^
5 errors generated.
Target //software/thunderscope:thunderscope_main failed to build
Use --verbose_failures to see the command lines of failed build steps.
ERROR: /Users/maggie/vm/Software/src/software/thunderscope/BUILD:13:10 Middleman _middlemen/software_Sthunderscope_Sthunderscope_Umain-runfiles failed: (Exit 1): cc_wrapper.sh failed: error executing CppCompile command (from target @@abseil-cpp+//absl/log/internal:structured_proto) external/rules_cc++cc_configure_extension+local_config_cc/cc_wrapper.sh -U_FORTIFY_SOURCE -fstack-protector -Wall -Wthread-safety -Wself-assign -Wunused-but-set-parameter -Wno-free-nonheap-object ... (remaining 86 arguments skipped)

Use --sandbox_debug to see verbose messages from the sandbox and retain the sandbox build root for debugging
INFO: Elapsed time: 40.967s, Critical Path: 26.03s
INFO: 167 processes: 1386 action cache hit, 12 internal, 155 darwin-sandbox.
ERROR: Build did NOT complete successfully
ERROR: Build failed. Not running target

@Andrewyx
Copy link
Contributor

Adding TODO: Specify minimum xcode version

Copy link
Contributor

@Andrewyx Andrewyx left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@sauravbanna sauravbanna left a comment

Choose a reason for hiding this comment

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

awesome !

}

is_darwin() {
if [[ $1 == "Darwin" ]]; then
Copy link
Contributor

Choose a reason for hiding this comment

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

very minor nit: add a comment here similar to line 15 in environment_setup/setup_software_mac.sh to explain what Darwin is

@Lmh-java Lmh-java merged commit 79b4a9f into UBC-Thunderbots:master Jan 24, 2026
10 of 11 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.

Explore M1 series macOS native support

8 participants