Configure the shared PlotJuggler JFrog Conan remote once per machine. Keeping it at index 0 lets Conan try cached PlotJuggler and third-party binaries before falling back to ConanCenter:
conan remote add plotjuggler-conan \
https://plotjuggler.jfrog.io/artifactory/api/conan/plotjuggler-conan \
--force --index=0
conan remote login plotjuggler-conan <user> -p <access-token>Then build:
# Build the full plugin collection
./build.shTo work on only one plugin, pass the plugin directory:
./build.sh data_load_csvRun ./build.sh --help to see the available arguments.
By default, build.sh installs the root conanfile.py and builds into
build/all/Release. With a plugin argument, it installs that plugin's
conanfile.py, configures CMake with -DPJ_BUILD_PLUGIN=<plugin_dir>, and
builds into build/<plugin_dir>/Release.
Each plugin directory has its own conanfile.py that lists
plotjuggler_sdk plus the plugin's own third-party deps. Keep it in sync
with the plugin's find_package(... REQUIRED) calls in CMakeLists.txt.
The root conanfile.py remains the full-repository dependency set for
local full builds and scheduled CI.
No extra steps — the parent project's build system handles everything (and
plotjuggler_sdk::plugin_sdk / ::plugin_host resolve to the in-tree
targets, so plugin CMakeLists.txt files write the same target_link_libraries
call in both modes):
cd /path/to/plotjuggler_sdk
./build.shEach plugin badge is a shields.io endpoint badge
backed by a small JSON file on the orphan badges branch — one
<plugin>.json per plugin holding { label, message: version, color }. A
native GitHub Actions badge can only report pass/fail for a whole workflow, so
this scheme is what lets a single badge carry both the version and the build
color.
CI publishes them on pushes to main:
- The CI Linux
per-plugin-buildmatrix builds each plugin in isolation; every leg emits its badge JSON (version frommanifest.json, color from that leg's outcome) as an artifact, and theupdate-badgesjob commits all of them to thebadgesbranch in one push. data_stream_ros2is built only by CI ROS2, so that workflow owns its badge and writes it from its ownupdate-badgejob (color aggregated from the distro matrix and the proxy build).
Both writers share a badges-branch concurrency group and resync-on-conflict
(see scripts/publish_badges.sh), so their pushes never clobber each other.
Badges first appear after the feature's initial run on main; until then
shields renders them as invalid because the badges branch does not yet
exist.
Both plugin families in this repo follow a declarative style on top of
the plotjuggler_sdk SDK: a DataSource hands the host a deferred byte
fetcher per message, and a MessageParser declares a table of schema
handlers that produce canonical objects (sdk::Image,
sdk::PointCloud, and related builtin types) plus scalar columns. The host
chooses eager vs lazy materialization per message without either plugin
caring.
See PLUGIN_DEVELOPMENT.md for the full
developer guide: the canonical-object vocabulary, the DataSource and
MessageParser shapes, end-to-end dispatch flow, authoring checklists,
and pointers to data_load_mcap and parser_ros as reference
implementations.
When adding or changing a plugin:
- Keep
manifest.jsoncurrent; the release tag version must match it. - Add or update the plugin's
CMakeLists.txt. - Add any Conan dependencies to the plugin's
conanfile.py. - Add new dependencies to the root
conanfile.pywhen full-repository builds need them. - Add focused tests in the plugin directory when behavior changes.
Each plugin is independently versioned and released. The release pipeline builds the tagged plugin on 6 platforms (Linux x86_64/aarch64, macOS Intel/ARM, Windows x64/ARM64), creates plugin-scoped release notes, and can automatically submit to the extension registry.
# One command: bump version, commit, tag, push, build, submit to registry
python3 scripts/release_extension.py foxglove-bridge --bump minor --submit-to-registryThis will:
- Update
manifest.jsonwith new version - Commit and push the change
- Create annotated tag → triggers CI
- CI installs that plugin's Conan recipe, builds it on all 6 platforms, and creates a GitHub Release with notes from that plugin's directory
- Automatically creates a
pj-plugin-registryPR for the exact version in the triggering tag
When manifest already has the correct version (e.g., bumped in a previous commit):
# No --bump or --version: reads version from manifest, creates tag only
python3 scripts/release_extension.py foxglove-bridge --submit-to-registryUseful for batch releases or re-creating tags after cleanup.
<source_directory>/v<semver>
Examples: data_load_csv/v1.0.6, parser_ros/v2.1.0
The source directory before /v controls the CI build scope. A
data_load_csv/v1.0.6 tag installs data_load_csv/conanfile.py and configures
CMake with -DPJ_BUILD_PLUGIN=data_load_csv; it does not install or compile
dependencies for unrelated plugins. CI uses the same build.sh entry point as
local standalone builds.
| Script | Purpose |
|---|---|
release_extension.py |
Bump version, create tag, trigger CI |
submit_to_registry.py |
Submit release to extension registry |
release_tools.py |
Validation and packaging utilities |
Full documentation: scripts/README.md — detailed pipeline diagram, CLI reference, troubleshooting.
plotjuggler_sdk is consumed as a Conan package from the plotjuggler
cloudsmith remote — no CPM source clone, no SSH deploy key, no
subdirectory-mode fallback for standalone builds. Every per-plugin
conanfile.py also lists it so single-plugin builds resolve it the same way.
The version is pinned in one place — the top-level SDK_VERSION file — and CI
builds core from the pinned extern/plotjuggler_core submodule when cloudsmith
is unavailable (scripts/ensure_core.sh).
Repository & package rename: the SDK source now lives in the plotjuggler_sdk repository (formerly
plotjuggler_core), and the Conan package and CMake targets are renamed to match — recipes requireplotjuggler_sdk/<version>and linkplotjuggler_sdk::plugin_sdk/::plugin_host. The only thing that keeps the old name is the submodule mount point,extern/plotjuggler_core.
| Package | Version | Used by |
|---|---|---|
| plotjuggler_sdk (cloudsmith) | pinned via SDK_VERSION (exact) |
SDK + host loaders (plotjuggler_sdk::plugin_sdk, ::plugin_host) |
| nlohmann_json | 3.12.0 | Most plugins |
| arrow + parquet | 23.0.1 | data_load_parquet, data_load_rerun, toolbox_mosaico |
| paho-mqtt-cpp | 1.5.3 | data_stream_mqtt |
| cppzmq | 4.11.0 | data_stream_zmq |
| protobuf | 6.33.5 | parser_protobuf |
| zstd | 1.5.7 | data_load_mcap, data_stream_pj_bridge |
| lz4 | 1.10.0 | data_load_mcap |
| date | 3.0.4 | data_load_csv, data_load_mp4, data_load_parquet |
| ffmpeg | 8.1 | data_load_lerobot, data_load_mp4 |
| ixwebsocket | 11.4.6 | data_stream_foxglove_bridge, data_stream_pj_bridge |
| asio | 1.28.2 | data_stream_udp |
| liblsl | 1.16.2 | data_stream_lsl |
| kissfft | 131.1.0 | toolbox_fft |
| lua | 5.4.6 | toolbox_colormap, toolbox_reactive_scripts_editor, toolbox_mosaico |
| sol2 | 3.5.0 | toolbox_colormap, toolbox_reactive_scripts_editor, toolbox_mosaico |
| pybind11 | 2.13.6 | toolbox_reactive_scripts_editor |
| cpython | 3.12.7 | toolbox_reactive_scripts_editor |
| libdatachannel | 0.24.0 | data_stream_webrtc |
| fmt | 12.1.0 | data_load_mp4, toolbox_mosaico |
| zlib | 1.3.1 | data_load_mf4 (mdflib; matches arrow's pin) |
| expat | 2.6.4 | data_load_mf4 (mdflib) |
| gtest | 1.17.0 | All plugin tests |
| Package | Used by |
|---|---|
| ulog_cpp | data_load_ulog |
| rosx_introspection | parser_ros |
| data_tamer | parser_ros, parser_data_tamer |
| mdflib | data_load_mf4 |
| dbc_parser_cpp + fast_float | data_load_mf4, data_load_blf (shared common/can_dbc) |
| lblf | data_load_blf |
| Package | Version | Reason |
|---|---|---|
| libsodium | 1.0.20 | 1.0.21 has broken ARM NEON code that fails with GCC on aarch64 |
| lz4 (root aggregate build only) | 1.9.4 | Must equal arrow/23.0.1's own lz4 pin on Linux (Flight build), or Conan splits Arrow into a second build; data_load_mcap's own standalone recipe still pins lz4/1.10.0 unaffected |