Skip to content

feat(osi): replace pre-built OSI 3.5.0 with source-built OSI 3.8.0 via asam-osi-utilities#784

Open
jdsika wants to merge 13 commits into
esmini:devfrom
jdsika:feat/osi-utilities-integration
Open

feat(osi): replace pre-built OSI 3.5.0 with source-built OSI 3.8.0 via asam-osi-utilities#784
jdsika wants to merge 13 commits into
esmini:devfrom
jdsika:feat/osi-utilities-integration

Conversation

@jdsika

@jdsika jdsika commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace esmini's pre-built OSI 3.5.0 binaries (externals/osi/) with source-built OSI 3.8.0 via asam-osi-utilities as a Git submodule. This modernizes the OSI dependency chain, gives esmini access to the latest OSI features (proto3 optional fields, new message types), and provides a reproducible build via vcpkg.

Motivation

  • OSI 3.5.0 is outdated: The pre-built binaries shipped in externals/osi/ were frozen at OSI 3.5.0 with protobuf 3.x. Upstream OSI has moved to 3.8.0 with protobuf 5.x, proto3 optional fields, and new message types.
  • Pre-built binaries are fragile: Platform-specific .a/.lib files checked into the repo create ABI compatibility issues, complicate CI, and make it impossible to debug or rebuild OSI locally.
  • asam-osi-utilities is the canonical build system: The ASAM OSI ecosystem now provides asam-osi-utilities with proper CMake exports, vcpkg integration, and trace file I/O. This PR adopts it as the single source of truth for OSI.

Architecture: Two-Phase Build

The integration uses a two-phase build pattern:

Phase A -- Build OSI dependencies (cached in CI)

cmake -S externals/asam-osi-utilities -B build-osi-deps \
  -DCMAKE_TOOLCHAIN_FILE=<vcpkg>/scripts/buildsystems/vcpkg.cmake \
  -DVCPKG_TARGET_TRIPLET=<triplet>
cmake --build build-osi-deps --config Release
cmake --install build-osi-deps --prefix osi-deps/

Phase A resolves protobuf, abseil, and other transitive dependencies via vcpkg, then builds and installs OSI + utilities into a prefix directory. This is cached in CI -- subsequent runs skip this entirely.

Phase B -- Build esmini (uses Phase A output)

cmake . -B build \
  -DOSI_UTILITIES_PREFIX=osi-deps/ \
  -DCMAKE_PREFIX_PATH="osi-deps/;<vcpkg_installed>/<triplet>" \
  -DProtobuf_PROTOC_EXECUTABLE=<vcpkg_installed>/<triplet>/tools/protobuf/protoc

Phase B consumes the installed OSI libraries via standard CMake find_package(). No vcpkg toolchain needed -- just CMAKE_PREFIX_PATH pointing to the install prefix.

Why two phases?

  1. Separation of concerns: vcpkg manages OSI's complex dependency tree (protobuf, abseil, utf8-range, zlib); esmini's own build stays clean with no vcpkg toolchain dependency.
  2. Cache efficiency: Phase A output (~50 MB) is cached by hash of externals/asam-osi-utilities/** + triplet files. Rebuilds only when the submodule or triplets change.
  3. Protobuf descriptor pool isolation: On Windows, static linking (x64-windows-static-md triplet) ensures each DLL/EXE gets its own protobuf descriptor pool, preventing double-registration crashes when esminiLib.dll and test executables both link OSI.

Commits

Commit Description
cc78b43d feat(osi): replace pre-built OSI 3.5.0 with source-built OSI 3.8.0 -- CMake integration, source fixes, Python bindings, vcpkg triplets
d2bbc759 ci: integrate asam-osi-utilities two-phase build -- Reusable composite action, workflow updates for ci/memoryleak/sanitize
2171149f docs: update documentation for OSI 3.8.0 and two-phase build -- README, user guide, release notes
df0139a3 build: add CMake presets and simplify CI workflows -- CMakePresets.json with default/full/ci-* presets, env-var injection
e03188f3 build(devcontainer): add zip and vcpkg for OSI two-phase build -- vcpkg bootstrap at /opt/vcpkg
53a41ad1 build(devcontainer): add system deps, proxy forwarding, and build docs -- dev packages, proxy env, protobuf conflict docs, README two-phase example
eaa751a4 fix(osi): scope MSVC warning suppression and harden OSI tests -- Per-target /wd flags via osi_external_warnings INTERFACE library, resilient OSI file size assertions, auto-derive vcpkg commit from submodule
6f028913 fix(osi): clean up stale OSI 3.5 references for OSI 3.8 migration -- Modernize FMU CMakeLists, remove dead DYN_PROTOBUF blocks, update Python comments and tutorial docs
fe912add refactor(osi): replace deprecated OSI enum aliases with modern names -- TYPE_MEDIUM_CAR->TYPE_CAR, TYPE_MOTORBIKE->TYPE_MOTORCYCLE, TYPE_DELIVERY_VAN->TYPE_VAN, COLOR_GREY->COLOR_GRAY
e536e2a4 ci(osi): add restore-keys for partial cache fallback -- 2-level fallback chain for Phase A cache
51a7e407 ci(osi): add nightly cache-warming workflow -- Prevents 7-day GitHub Actions cache eviction on quiet periods
e92a4416 ci(osi): enable ccache for Linux and macOS CI builds -- Compiler-level caching for Phase B incremental builds
c5d06a0e ci(osi): enable vcpkg binary caching via GitHub Actions cache -- Package-level caching via x-gha provider for Phase A cold misses

Design Decisions

1. Git submodule for asam-osi-utilities

Decision: Add externals/asam-osi-utilities as a Git submodule pointing to the main branch.

Rationale: Submodules provide a pinned, reproducible dependency without vendoring large build systems. The asam-osi-utilities repo includes osi-cpp and mcap as nested submodules, giving esmini the full OSI + trace file stack from a single entry point.

2. Custom vcpkg triplets

  • support/vcpkg-triplets/universal-osx.cmake: Builds universal (arm64 + x86_64) fat binaries on macOS so vcpkg-managed protobuf/abseil match esmini's universal builds.
  • support/vcpkg-triplets/x64-windows-static-md-v142.cmake: Uses MSVC v142 toolset (VS 2019). esmini's CI uses -T v142 for backward compatibility; vcpkg defaults to v143 whose STL intrinsics aren't available in v142.

3. find_package(Protobuf CONFIG REQUIRED) in osi.cmake

CMake's built-in FindProtobuf module does not resolve transitive dependencies (abseil, utf8-range) from vcpkg's package configs. Using CONFIG mode ensures the vcpkg-installed protobuf-config.cmake is used, which properly declares all transitive targets.

4. MSVC warning suppressions (/wd4141, /wd4267, etc.)

Suppress specific protobuf/abseil header warnings on MSVC, scoped to OSI-consuming targets only. An osi_external_warnings INTERFACE library in osi.cmake carries the 6 /wd flags, and a wrapper osi_with_warnings target propagates them automatically to anything linking OSI_LIBRARIES. Four include-only targets (ScenarioEngine, ViewerBase, PlayerBase, Controllers) explicitly link osi_external_warnings. This keeps the warnings active for esmini's own code while suppressing them where protobuf/abseil headers are compiled. /w14296 remains removed from compiler_warnings.cmake because MSVC's last-flag-wins semantics would override the per-target /wd4296 suppression.

MSVC's /external:W0 flag has a known bug that fails to suppress some warnings from SYSTEM include directories, and template instantiation in esmini's own TUs fires warnings from protobuf headers regardless.

Warning Description Source
C4141 'inline' used more than once message_lite.h
C4267 size_t narrowing conversion repeated_field.h
C4244 narrowing conversion protobuf headers
C4189 local variable initialized but unused repeated_field.h
C4296 expression is always true abseil variant.h
C4459 declaration hides global abseil hash.h

5. Traffic sign enum validation

Add TrafficSign_MainSign_Classification_Type_IsValid() check before static_cast in OSIReporter.cpp. When a traffic sign type is not found in the OpenDRIVE-to-OSI mapping table, osi_type defaults to INT_MAX. In Debug builds, protobuf asserts _IsValid(value) and crashes. The fix validates before casting and falls back to TYPE_OTHER with a log warning.

6. Proper protobuf API for vehicle type in osi2csv.py

Replace str(o.vehicle_classification)[11:-1] with MovingObject.VehicleClassification.Type.Name(type_enum). OSI 3.8.0 changed VehicleClassification fields to proto3 optional (explicit presence tracking), breaking the fragile string-slicing approach.

7. Replace vendored osi3 Python bindings with osi-python

Delete vendored scripts/osi3/*.py files. In CI, osi-python is installed from the submodule tree (pip install externals/asam-osi-utilities/submodules/osi-python). The vendored bindings were generated for OSI 3.5.0 / protobuf 3.x and are incompatible with protobuf 5.x wire format.

8. CI cache strategy (split restore/save)

Use actions/cache/restore@v4 + actions/cache/save@v4 instead of actions/cache@v5 with save-always. The split approach saves the cache immediately after Phase A install (before downstream tests can fail), ensuring the cache is always populated on the first successful Phase A build.

9. fail-fast: false on the test matrix

Disable fail-fast so that a failure on one platform doesn't cancel in-progress jobs on other platforms. During this integration (which touches all 3 platforms), getting results from all platforms simultaneously is critical for efficient debugging.

10. Phase A configuration on Windows vs Linux/macOS

On Windows, Phase A uses matrix.configuration (Debug or Release). On Linux/macOS, Phase A always builds Release. MSVC static libraries encode _ITERATOR_DEBUG_LEVEL and CRT linkage (MD vs MDd), so a Release Phase A library cannot link into a Debug Phase B build. On Linux/macOS, static libraries are configuration-agnostic, so a single Release build serves both configurations. This means Windows has 2 Phase A caches (Debug + Release) while Linux/macOS have 1.

11. Protobuf version management

The C++ protobuf version (currently v5.29.3) is controlled by the vcpkg baseline in externals/asam-osi-utilities/vcpkg-configuration.json. The Python protobuf version (currently >=6.30.2) is declared by osi-python's pyproject.toml. These are not centrally synchronized, but wire-format compatibility across protobuf major versions ensures interoperability between C++ and Python. The requirements.txt no longer pins protobuf directly -- it is pulled transitively via osi-python.

12. Auto-derive vcpkg commit from submodule

The setup_osi_utilities composite action no longer hardcodes the vcpkg commit hash. Instead, it extracts the baseline from externals/asam-osi-utilities/vcpkg-configuration.json via jq at runtime. This eliminates the risk of the action and submodule drifting out of sync when the submodule is updated. An explicit vcpkg-commit input can still override this for special cases.

13. Resilient OSI file size assertions

Unit tests previously used exact EXPECT_EQ(fileStatus.st_size, N) assertions that break on any protobuf serialization change. These are replaced with range-based checks (EXPECT_GT/EXPECT_LT) that validate the test behavior (file was written, grew with frames, mode differences are significant) without depending on exact byte counts. The lane_no_obj test already used this pattern; now all 6 GroundTruth file-size tests follow suit.

Files Changed (77 files, +1372 -1393)

Core CMake integration

  • CMakeLists.txt -- Replace DYN_PROTOBUF option with OSI_UTILITIES_PREFIX; remove OSI download step
  • support/cmake/external/osi.cmake -- Complete rewrite: find_package(open_simulation_interface) via installed prefix instead of manual lib/include paths
  • support/cmake/common/locations.cmake -- Remove EXTERNALS_OSI_PATH and OS-specific OSI paths
  • support/cmake/common/definitions.cmake -- Remove DYN_PROTOBUF definitions
  • support/cmake/common/flags.cmake -- Protobuf-specific /wd flags moved to per-target osi_external_warnings in osi.cmake; only /wd4127 and /wd4100 remain global
  • support/cmake/rule/compiler_warnings.cmake -- /w14296 stays removed (MSVC last-flag-wins would override per-target /wd4296)

CI / GitHub Actions

  • .github/actions/setup_osi_utilities/action.yml -- New reusable composite action for two-phase build with caching; auto-derives vcpkg baseline from submodule config via jq
  • .github/actions/setup_tools_shared/action.yml -- Add osi-python and asam-osi-utilities Python package installation from submodule tree
  • .github/workflows/ci.yml -- Integrate Phase A, custom triplets, fail-fast: false, platform-specific configs
  • .github/workflows/memoryleak.yml -- Add Phase A setup
  • .github/workflows/sanitize.yml -- Add Phase A setup

Source fixes

  • OSIReporter.cpp -- Validate traffic sign enum before set_type(), fall back to TYPE_OTHER
  • scripts/osi2csv.py -- Use proper protobuf API for vehicle type extraction
  • test/smoke_test.py -- Update OSI assertions for 3.8.0 type aliases (MEDIUM_CAR -> CAR)
  • ScenarioEngineDll_test.cpp -- Replace hardcoded OSI file size assertions with resilient range-based checks (EXPECT_GT/EXPECT_LT); increase max_msg_size buffer

Dependency management

  • .gitmodules -- Add externals/asam-osi-utilities submodule (recursive)
  • externals/asam-osi-utilities -- Submodule at b7c8603 (main)
  • support/python/requirements.txt -- Remove standalone protobuf pin (now transitive via osi-python)
  • scripts/osi3/ -- Delete all 11 vendored protobuf bindings (replaced by osi-python package)
  • support/vcpkg-triplets/universal-osx.cmake -- macOS universal binary triplet
  • support/vcpkg-triplets/x64-windows-static-md-v142.cmake -- Windows v142 toolset triplet

Dev Container

  • .devcontainer/Dockerfile -- Add system dev packages (lz4-devel, libzstd-devel, libX11-devel, libXrandr-devel, libXinerama-devel, mesa-libGL-devel, fontconfig-devel), vcpkg bootstrap, protobuf version conflict documentation
  • .devcontainer/devcontainer.json -- Forward HTTP_PROXY/HTTPS_PROXY/NO_PROXY via containerEnv for vcpkg registry fetch behind corporate proxies
  • .gitignore -- Add CMakeFiles/ to ignored paths

Documentation

  • README.md -- Update OSI version (3.5.0 -> 3.8.0), protobuf now vcpkg-managed, two-phase build example for Windows and Linux/macOS
  • docs/user_guide.adoc -- Rewrite build guide with --recursive and two-phase instructions; replace stale DYN_PROTOBUF section with static/dynamic linking guide and protobuf/OSI version documentation; simplify Python OSI setup; update OSI doc links to latest
  • release_notes.md -- Add OSI 3.8.0 migration entry
  • .github/copilot-instructions.md -- Build instructions for the new two-phase workflow

Testing

  • Unit tests: Updated 6 message size expectations for OSI 3.8.0 (proto3 optional fields change serialization sizes)
  • Smoke tests: Updated vehicle type assertions (MEDIUM_CAR -> CAR due to OSI 3.8.0 enum alias resolution)
  • All existing tests pass on all 3 platforms in both Debug and Release configurations

Breaking Changes

  • DYN_PROTOBUF CMake option removed: Dynamic protobuf linking via externals/OSI/*/lib-dyn is no longer supported. For shared linking scenarios, use LINK_WITH_SHARED_OSI=ON with dynamic vcpkg triplets -- see asam-osi-utilities integration docs.
  • externals/osi/ directory no longer used: Pre-built OSI binaries replaced by submodule-based build.
  • New build prerequisite: Building with USE_OSI=ON now requires a Phase A pre-build step (automated in CI, documented for local builds in user guide).
  • Python protobuf pin removed from requirements.txt: protobuf is now a transitive dependency of osi-python, installed from the submodule.

How to Build Locally

# Clone with submodules
git clone --recursive https://github.com/esmini/esmini.git

# Phase A: Build OSI dependencies (via vcpkg)
cmake -S externals/asam-osi-utilities -B build-deps --preset vcpkg-windows-static-md  # Windows
cmake -S externals/asam-osi-utilities -B build-deps --preset vcpkg                    # Linux/macOS
cmake --build build-deps --config Release
cmake --install build-deps --config Release --prefix osi-deps

# Phase B: Build esmini (set env vars from Phase A output, then use "full" preset)
# Windows (cmd):
set ESMINI_OSI_PREFIX=osi-deps
set ESMINI_CMAKE_PREFIX_PATH=osi-deps;build-deps/vcpkg_installed/x64-windows-static-md
set ESMINI_PROTOC=build-deps/vcpkg_installed/x64-windows-static-md/tools/protobuf/protoc.exe
cmake --preset full
cmake --build build --config Release --target install

# Linux/macOS (bash):
export ESMINI_OSI_PREFIX=osi-deps
export ESMINI_CMAKE_PREFIX_PATH="osi-deps;build-deps/vcpkg_installed/x64-linux"
export ESMINI_PROTOC=build-deps/vcpkg_installed/x64-linux/tools/protobuf/protoc
cmake --preset full
cmake --build build --config Release --target install

# Or build without OSI (no Phase A needed)
cmake --preset default
cmake --build build --config Release --target install

Known Issues (Dev Container)

  • Protobuf version conflict: The base image (ghcr.io/bounverif/esmini:latest-devel) ships protobuf 3.21.12 at /opt/bazalt/. Using the vcpkg preset for Phase A causes header conflicts. Use the base preset with -DCMAKE_PREFIX_PATH=/opt/bazalt instead.
  • MCAP lz4 + GCC 14: mcap/writer.inl:179 has an implicit int to LZ4F_blockChecksum_t enum conversion rejected by GCC 14 with system lz4 1.8.3. Workaround: add -DCMAKE_CXX_FLAGS=-fpermissive to Phase A configure.
  • Proxy required for vcpkg: HTTP_PROXY/HTTPS_PROXY must be set inside the container for vcpkg registry fetch. The updated devcontainer.json forwards these from the host automatically.

Related PRs

@jdsika jdsika force-pushed the feat/osi-utilities-integration branch 7 times, most recently from 9b14c70 to ba27216 Compare March 28, 2026 19:47
@jdsika jdsika changed the title feat(osi): replace pre-built OSI with asam-osi-utilities integration feat(osi): replace pre-built OSI 3.5.0 with source-built OSI 3.8.0 via asam-osi-utilities Mar 28, 2026
@jdsika jdsika marked this pull request as ready for review March 28, 2026 21:33
@eknabevcc eknabevcc changed the base branch from master to dev March 29, 2026 10:03
@eknabevcc

Copy link
Copy Markdown
Collaborator

We will carefully review and consider this updated and improved OSI integration.

We have considered updating OSI before, but so far decided to stay due to 3rd party dependencies. It's some time ago, so maybe that decision can be re-evaluated. In which case I think this PR can be extremely valuable.

A quick comment on the fixed static libraries; The idea is not to have esmini locked into those. For example, any user could replace the OSG libraries with customized ones. However, they're a bit tricky and time consuming to build so the provided ones can be considered and used as default ones.

For reviewing this PR, there will be a few considerations, e.g:

  • would it be possible to re-add the option for dynamic linking of OSI/Protobuf?

    My experience, so far, is that dynamic linking sometimes is necessary when involving (statically linking) 3rd party libs that also have OSI/protobuf dependencies. To overcome issues with linking two libs, each statically linking OSI, they both link OSI/Protobuf dynamically (same lib). Further, this dependency (to the 3rd party lib) COULD imply a requirement to stay at specific OSI version.

  • would it be possible to support multiple versions, i.e. parameterize OSI (and perhaps Protobuf) version(s)?

    I can imagine supporting multiple versions could be tricky. In which case deciding on version will require even more careful investigation. That said, hopefully we can easily conclude that 3.8 would work.

@jdsika

jdsika commented Mar 29, 2026

Copy link
Copy Markdown
Contributor Author

OK, thanks for the requirements. I will see how to proceed. This PR is in preparation to introduce writing trace files into .mcap which is part of ASAM-OSI-Utilities. Then Lichtblick visualisation can be used natively which is a great win!
https://github.com/lichtblick-suite/lichtblick

In addition we can ease the integration of OSC-Validation.

There is no hurry to merge. I will also see how to further improve caching for lower build times.

I was just happy to get it a green ci yesterday :)))

@jdsika

jdsika commented Mar 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thoughtful review and clear requirements, @eknabevcc! Both points are addressable within the current architecture. Here's my technical plan:


1. Dynamic linking of OSI/Protobuf

Good news: the underlying osi-cpp library (bundled in asam-osi-utilities) already builds all three variants unconditionally:

Target Type Use case
open_simulation_interface_static Static Pure static executables
open_simulation_interface_pic Static + PIC Current default — for use in shared libraries
open_simulation_interface Shared (.so/.dylib/.dll) Dynamic linking scenario

The current PR always selects _pic (static). Re-adding dynamic support requires:

  1. New CMake option DYN_OSI (or re-use the familiar DYN_PROTOBUF name) — controls which OSI target is linked
  2. Dynamic vcpkg triplets — protobuf/abseil linkage is controlled by VCPKG_LIBRARY_LINKAGE in the triplet file. I'll add x64-linux-dynamic, x64-windows-dynamic, etc. alongside the existing static ones
  3. Target selection in osi.cmake — when DYN_OSI=ON, link open_simulation_interface::open_simulation_interface (shared) instead of _pic (static)
  4. RPATH handling — automatic via CMake target properties (no more manual RPATH code)

The key use case you described — two libs each needing OSI, avoiding double-registration — maps exactly to the shared OSI + shared protobuf configuration. Both libs would link against the same .so/.dylib at runtime.

Implementation: I'll add this as a follow-up commit on this PR. The CI will test static (default) on all platforms; dynamic can be validated as an opt-in.


2. OSI and Protobuf version parameterization

The version control already exists in the architecture, but isn't well-documented:

OSI version is controlled by the osi-cpp git submodule commit:

  • externals/asam-osi-utilities/submodules/osi-cpp → pinned at a specific commit
  • Version auto-detected from open-simulation-interface/VERSION file (currently 3.8.0)
  • To use a different OSI version: update the submodule to the desired tag, rebuild Phase A
  • No code changes needed — proto files are compiled from source at build time

Protobuf version is controlled by the vcpkg baseline:

  • vcpkg-configuration.json pins a vcpkg registry commit that determines available package versions
  • Currently resolves protobuf 5.29.x from the baseline
  • To pin a specific version: add version constraint in vcpkg.json, or use a different baseline commit
  • To use system protobuf: skip vcpkg entirely, point CMAKE_PREFIX_PATH to your protobuf install

Important constraint: Protobuf is a public dependency — its types appear in OSI headers. All consumers must use the same protobuf version (ABI-compatible). This is inherent to protobuf, not specific to this integration.

What I'll add:

  1. OSI_VERSION and PROTOBUF_VERSION as informational CMake variables printed during configure
  2. Documentation (in copilot-instructions and/or README) explaining how to pin specific versions
  3. A vcpkg overlay mechanism for users who need a specific protobuf version

True simultaneous multi-version support (e.g., build for both OSI 3.5 and 3.8 at once) would be impractical since the proto message definitions differ. But single-version parameterization is straightforward.


Timeline

I'll address both points in follow-up commits on this branch:

  1. DYN_OSI option + dynamic triplets
  2. Version documentation + introspection variables

The existing green CI and test coverage provide a solid baseline. No rush — I want to get this right.

@jdsika

jdsika commented Mar 31, 2026

Copy link
Copy Markdown
Contributor Author

@eknabevcc this is ready for review

I will only further investigate caching improvements.

@jdsika jdsika force-pushed the feat/osi-utilities-integration branch 2 times, most recently from ba53fd2 to 3f0c18b Compare March 31, 2026 19:51
jdsika added 6 commits March 31, 2026 21:52
…a asam-osi-utilities

Replace the legacy pre-built OSI 3.5.0 static binaries with modern
source-built OSI 3.8.0 provided by the asam-osi-utilities library.

Key changes:
- Add asam-osi-utilities as a git submodule (externals/asam-osi-utilities)
- Rewrite support/cmake/external/osi.cmake to build OSI from source using
  vcpkg for dependency management (protobuf 5.x, abseil, lz4, zstd)
- Remove stale Python protobuf bindings (scripts/osi3/*_pb2.py) in favor
  of the osi-python package from asam-osi-utilities
- Update OSIReporter.cpp for protobuf 5.x API (arena allocation,
  GetDescriptor, traffic sign type validation)
- Update unit tests and smoke tests for OSI 3.8.0 message sizes and
  vehicle type enum aliases
- Add vcpkg triplets for macOS universal builds and Windows static-md
- Suppress MSVC warnings from protobuf/abseil third-party headers

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Add a reusable setup_osi_utilities composite action that builds
asam-osi-utilities with vcpkg in Phase A (cached separately) and
installs headers, libraries, and CMake configs to an osi-deps prefix.
Phase B (esmini itself) consumes this via CMAKE_PREFIX_PATH.

Changes:
- Add .github/actions/setup_osi_utilities/action.yml with two-phase
  build, vcpkg caching, and platform-specific triplet selection
- Update ci.yml, memoryleak.yml, sanitize.yml to call the new action
  before CMake configure, passing OSI_UTILITIES_PREFIX and vcpkg paths
- Add Python 3.x to setup_tools_shared for osi2csv smoke tests
- Replace deprecated actions/cache save-always with explicit
  cache/restore + cache/save pattern for Phase A artifacts

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Update all user-facing documentation to reflect the migration from
pre-built OSI 3.5.0 to source-built OSI 3.8.0 via asam-osi-utilities:

README.md:
- Update OSI version to v3.8.0, protobuf to vcpkg-managed v5.x
- Replace generate_osi_libs.sh reference with submodule build info

user_guide.adoc:
- Rewrite build guide with git clone --recursive and two-phase build
- Add quick build without OSI section
- Replace stale DYN_PROTOBUF section with comprehensive guide covering
  static linking (default), dynamic/shared linking (Linux/macOS),
  protobuf version configuration, and OSI version pinning
- Simplify Python OSI setup (pip install from requirements.txt)
- Update OSI doc links from V3.5.0 to latest
- Remove stale generate_osi_libs.sh and Dropbox download references

release_notes.md:
- Document the OSI 3.5.0 to 3.8.0 migration as a new feature

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Add CMakePresets.json (version 6) to centralize build configuration:
- Hidden base presets (base, osi-base, no-externals-base)
- Platform-specific CI presets (ci-linux, ci-macos, ci-windows)
- Slim build presets (ci-no-externals-{linux,macos,windows})
- Specialized presets (ci-sanitize, ci-memoryleak)
- Developer presets (default, full)

OSI paths (OSI_UTILITIES_PREFIX, CMAKE_PREFIX_PATH, Protobuf_PROTOC_EXECUTABLE)
are injected via environment variables (ESMINI_OSI_PREFIX, ESMINI_CMAKE_PREFIX_PATH,
ESMINI_PROTOC) set by the setup_osi_utilities composite action.

CI workflows now use 'cmake --preset <name>' instead of per-platform
configure steps with duplicated flags. This reduces the test job configure
from 3 platform-specific steps to 1 cross-platform step.

Preset conditions restrict platform-specific presets to their target OS,
so 'cmake --list-presets' only shows relevant options.

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Extend the devcontainer Dockerfile with:
- zip package (required by vcpkg bootstrap, unzip already present)
- vcpkg bootstrapped at /opt/vcpkg with VCPKG_ROOT set

This allows users to immediately run Phase A of the OSI two-phase build
inside the devcontainer without manual vcpkg setup.

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
- Add missing system dev packages to Dockerfile (lz4-devel, libzstd-devel,
  libX11-devel, libXrandr-devel, libXinerama-devel, mesa-libGL-devel,
  fontconfig-devel) required for full build with OSG/ImPlot
- Document protobuf version conflict and base preset workaround
  (base image ships protobuf 3.21.12, vcpkg installs 5.29.3)
- Forward HTTP_PROXY/HTTPS_PROXY/NO_PROXY via containerEnv in
  devcontainer.json for vcpkg registry fetch behind corporate proxies
- Expand README two-phase build instructions with concrete Phase A/B
  example for both Windows and Linux/macOS

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
@jdsika jdsika force-pushed the feat/osi-utilities-integration branch 2 times, most recently from 745c605 to a0c6277 Compare April 1, 2026 10:32
Scope protobuf/abseil MSVC warning suppressions to OSI-consuming targets
instead of applying them globally. This restores warnings 4141, 4267,
4244, 4189, 4296, and 4459 for esmini's own code while keeping them
suppressed where OSI headers are compiled.

Key changes:
- Create osi_external_warnings INTERFACE library in osi.cmake carrying
  the 6 protobuf-specific /wd flags, and wrap OSI_LIBRARIES so linking
  targets inherit them automatically
- Remove those 6 /wd flags from global add_compile_options() in
  flags.cmake (keep /wd4127 and /wd4100 for general esmini patterns)
- Add osi_external_warnings to 4 include-only targets (ScenarioEngine,
  ViewerBase, PlayerBase, Controllers)
- Restore /w14296 in compiler_warnings.cmake (no esmini code triggers
  it; only protobuf/abseil headers do, now scoped)
- Replace 6 hardcoded EXPECT_EQ(st_size, N) assertions with resilient
  range-based checks (EXPECT_GT/LT) and increase max_msg_size buffer
- Auto-derive vcpkg commit from submodule vcpkg-configuration.json
  baseline via jq, eliminating manual synchronization risk
- Remove unused externals/implot from slim CI cache path

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
@eknabevcc

Copy link
Copy Markdown
Collaborator

After brief internal discussions, here's our initial ideas on strategy forward:

  • still use pre-built libraries
    This approach has served us and many others well. Are there any show-stoppers wrt OSI that blocks this approach? (we can discuss pros and cons later if needed)
    Can we update existing OSI build script (or create a second for 3.8) heavily inspired by your build process?
  • add esmini support for OSI 3.8
    We build OSI 3.8 for the three supported platforms, using the updated build script
    Short term, keep OSI 3.5 as the default and add a flag (cmake -D...) for switching to v3.8
    Second step, switch default to 3.8 (maybe few months)
    Third step, remove 3.5 (maybe 1 year, when all dependencies are updated as well)

@slundel6 already looking into this, and will continue next week. We are prepared to support to find a compromise solution serving the use cases you see and ours.

@jdsika

jdsika commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

Should we maybe setup a quick teams meeting to discuss it?

@eknabevcc

Copy link
Copy Markdown
Collaborator

Sounds like a good idea.
We're available next week Tue 12-15 and Wed 12.00-13.30.
I sent a temporary invitation. Feel free to propose another time slot.

@jdsika

jdsika commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

The main argument for this integration from my end would be:

  • OSI 3.8.0 is backwards compatible with OSI 3.5.0
    • Finding an incompatibility would mean a serious flaw in OSI or Protobuffer
    • I would still test all existing OSI features "by hand" but it should already work everything in this PR
  • I did imo a major improvement to the build process giving flexibility and control to the user e.g. regarding proto versions
  • ASAM-OSI-Utilities contain important helper functionality to read and write OSI single and multichannel trace files (major feature)
  • using the osi multi channel tracefile format mcap would allow the direct use of esmini with Lichtblick -> Major improvement for esmini users
  • OSC-Validation uses ASAM-OSI-Utilities as well which means a better integration and means for improving test cases for esmini

jdsika added 6 commits April 2, 2026 17:07
- OSMP_FMU: derive OSIVERSION from find_package(open_simulation_interface)
  instead of hardcoding 3.5.0; remove old externals/osi/ include/link paths;
  move configure_file after set_osi_libs() so version is available
- osi.cmake: expose OSI_VERSION after find_package for downstream use
- Remove dead DYN_PROTOBUF code blocks from 7 CMake files (option was
  removed but if-blocks remained as unreachable code)
- Update outdated protobuf==3.20.2 comments in 8 Python files to
  reference osi-python package
- Update Hello-World tutorial OSI section for two-phase build

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
- TYPE_MEDIUM_CAR -> TYPE_CAR (same wire value)
- TYPE_MOTORBIKE -> TYPE_MOTORCYCLE (same wire value)
- TYPE_DELIVERY_VAN -> TYPE_VAN (same wire value)
- COLOR_GREY -> COLOR_GRAY (same wire value)

All replacements are pure renames with identical protobuf integer
values, so no behavioral change occurs.

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
When the exact Phase A cache key misses (e.g. after a minor submodule
update or triplet tweak), the restore step now falls back to the most
recent cache matching the same OS+triplet+config prefix, and then the
same OS+triplet. This lets vcpkg reuse already-downloaded packages
instead of fetching everything from scratch, significantly reducing
Phase A rebuild time on partial cache misses.

Fallback chain:
  1. Exact key: OS-triplet-config-hash
  2. Same OS+triplet+config (any prior hash)
  3. Same OS+triplet (any config or hash)

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Add a scheduled workflow that pre-builds OSI utilities Phase A artifacts
on the dev branch at 03:00 UTC daily. This populates the GitHub Actions
cache for all 4 platform/config combinations (Linux Release, macOS
Release, Windows Debug, Windows Release) so the first PR of the day
always gets a cache hit.

Also supports workflow_dispatch for manual cache refresh after OSI
submodule updates.

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
Enable compiler caching (ccache) for Phase B esmini builds on Linux
and macOS to reduce incremental build times on cache hits.

Changes:
- Add ENABLE_CCACHE=ON to ci-linux, ci-macos, ci-sanitize, and
  ci-memoryleak CMake presets
- Install ccache if not pre-installed on the runner
- Persist ccache directory across runs via actions/cache with
  restore-keys fallback (OS + config prefix matching)
- Log ccache stats after build for diagnostics

Windows is excluded because the Visual Studio generator has limited
ccache/sccache compatibility. The ENABLE_CCACHE flag in ccache.cmake
degrades gracefully (prints a warning, continues without caching)
if the binary is not found.

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
When the Phase A install-prefix cache misses and vcpkg must compile
dependencies from source (protobuf, abseil, lz4, zstd, etc.), this
change enables vcpkg's built-in binary caching backed by the GitHub
Actions cache (x-gha provider).

This means individual vcpkg packages are cached independently:
- If only one package changes, the others are restored from cache
- This complements the full install-prefix cache (which is all-or-nothing)
- On a cold Phase A cache, this can save significant build time

Implementation:
- Export ACTIONS_CACHE_URL and ACTIONS_RUNTIME_TOKEN via github-script
  (required by the x-gha provider for authentication)
- Set VCPKG_BINARY_SOURCES=clear;x-gha,readwrite via GITHUB_ENV
  so it persists through configure and build steps

Signed-off-by: jdsika <carlo.van-driesten@bmw.de>
@jdsika

jdsika commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

@eknabevcc I think the caching is now well optimized.

@jdsika

jdsika commented Apr 2, 2026

Copy link
Copy Markdown
Contributor Author

CI Build Time Analysis: Cold vs Warm Cache

We ran the CI twice -- once cold (no Phase A cache), once warm (all caches populated) -- to measure the impact of the caching improvements in this PR.

Results (test jobs with OSI)

Job Cold (no cache) Warm (cached) Saved
test (windows, Debug) 45.2 min 17.7 min 27.5 min (61%)
test (windows, Release) 40.6 min 14.3 min 26.3 min (65%)
test (macos, Debug) 12.7 min 8.8 min 3.9 min (31%)
test (ubuntu, Release) 12.1 min 6.3 min 5.8 min (48%)
test (ubuntu, Debug) 11.7 min 8.6 min 3.1 min (26%)
test (macos, Release) 12.6 min 9.0 min 3.6 min (29%)

Results (no-external-modules jobs -- no OSI, baseline)

Job Cold Warm Delta
no-ext (windows, Debug) 8.8 min 8.7 min ~0
no-ext (ubuntu, Debug) 5.8 min 5.1 min ~0
no-ext (macos, Debug) 6.5 min 5.3 min ~0

No-external jobs don't use OSI, so no change expected -- confirms the improvement comes from Phase A caching.

Summary

  • Worst-case CI wall-clock drops from ~45 min to ~18 min (limited by Windows Debug)
  • Windows benefits most because Phase A (protobuf, abseil, lz4, zstd via vcpkg) takes ~25 min to build from source with the VS generator
  • Linux/macOS see ~3-6 min savings (Phase A is faster with Ninja + smaller dependency tree on these platforms)
  • After the first run populates caches, all subsequent runs on the same branch hit the cache -- Phase A is skipped entirely

Caching strategy (4 layers)

  1. Full Phase A install-prefix cache -- keyed on OS+triplet+config+source hash, with restore-keys fallback for partial matches
  2. Nightly cache warming (warm_osi_cache.yml) -- runs on dev at 03:00 UTC, prevents 7-day GitHub Actions cache eviction
  3. ccache -- compiler-level caching for Phase B (esmini build), enabled on Linux/macOS CI presets
  4. vcpkg binary caching -- individual package caching via x-gha provider, helps when Phase A prefix cache misses but individual packages haven't changed

@jdsika

jdsika commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Think about something:
Phase A build results should be available for download in a cloud.

@jdsika

jdsika commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Check if debug is necessary in cache warming:

          - os: windows-2022
            triplet: x64-windows-static-md-v142
            configuration: Debug

@jdsika

jdsika commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Note:
Try to use ASAM-OSI-Utilities vcpkg pesets for dynamic linking in phase A in CI

@jdsika

jdsika commented Apr 15, 2026

Copy link
Copy Markdown
Contributor Author

@eknabevcc and @slundel6 I have invited you to my fork as collaborators for you to modify the branch yourself

@slundel6

Copy link
Copy Markdown
Collaborator

@jdsika thanks! We are working on your PR in a separate repo, approaching it from the angle we discussed. Our goal is to have an implementation that works to run, and then have a joint discussion about it.

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