Skip to content

Set library version with SDK language #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

bryfox
Copy link
Contributor

@bryfox bryfox commented Mar 14, 2025

Changelog

The SDK language is now reflected in the library written to MCAP files, and is included in serverInfo metadata under the key fg-library.

Description

Example: foxglove-sdk-python/v0.3.0.

The language name is set at build time, based on environment. For C++, which is a wrapper around the C library, a function is called by the log sinks when they're created to override that language.

The app may use the serverInfo metadata for troubleshooting or performance analysis. Foxglove has some precedent for the "fg-" prefix in user agents and HTTP header keys. "fg-library" echoes the "library" header of mcap.

Copy link

linear bot commented Mar 14, 2025

gasmith
gasmith previously approved these changes Mar 14, 2025
Comment on lines 84 to 97
- name: Build C library for ${{ matrix.target }}
env:
FOXGLOVE_SDK_LANGUAGE: c
run: cargo build --release
working-directory: c
- name: Ensure generated files are up to date
run: git diff --exit-code

- name: Build C++ library and run tests
if: ${{ !fromJson(matrix.cross) }}
env:
FOXGLOVE_SDK_LANGUAGE: cpp
run: make test
working-directory: cpp
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is going to work – the C++ sdk is just a wrapper for better ergonomics of calling the C sdk. The user will link the C sdk with their program and use the wrappers to call functions from it. So there is not really a "separate" build of the two SDKs (we aren't currently planning to ship a prebuilt C++ library, only C)

Copy link
Member

@jtbandes jtbandes Mar 14, 2025

Choose a reason for hiding this comment

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

As for alternatives, some ideas:

  • If this is used in specific places (like websocket server & mcap writer), the c++ sdk wrappers could pass in some value when creating those to override the defaults (but we'd have to remember to do this in every relevant location)
  • We could add a global function to set the library name at runtime (though would have to decide when to call it? might end up being pretty horrendous to do this in a cross-platform way, unless we want to make the users just call some init function explicitly)
  • I really hope someone else can come up with a better 3rd option 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I completely missed that this step doesn't run cargo build. Runtime it is.

I thought about something similar to the second idea; in python, it seems reasonable for us to call a function during the module initialization. I'll see if I can find something that seems workable across the board.

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'm leaning towards a hybrid approach: introduce a compiled_sdk_language build-time environment variable to replace what's here, and then have a static init step in c++ which can override this. Then at runtime, build a string with the compiled version. That sidesteps the cross-platform issue you linked, and I'd like to avoid providing something in the API to set the library name / language directly.

When I tried this out, I get a cmake error — something internal to Catch2 — unless I skip building the tests. I'll have to solve whatever issue that is (assuming it's not uncovering a legitimate problem), but would like your thoughts on the approach before spending more time on it.

Copy link
Member

@jtbandes jtbandes Mar 21, 2025

Choose a reason for hiding this comment

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

I guess I'm not sure what you mean by a static init step, you mean just forcing the user to call something like foxglove::initSDK()? I guess that's fine. We could also call it ourselves each time a Channel is constructed, as an example

also, for the Catch2 issue, maybe try rebasing - I fixed something related to that recently but it was a linker error, not a cmake error, so maybe different from what you ran into

Copy link
Member

Choose a reason for hiding this comment

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

Another alternative is that we could simply accept that we only have one string which is used for both C/C++ SDKs. Then assume probably 99%+ of users would be using it via C++. I guess it depends exactly what problem we are trying to solve here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I'm not sure what the cmake error was, but it eventually went away after commenting/uncommenting things in the file (maybe something cached?). Initially I was thinking I could statically initialize something inside an unnamed namespace to take care of this, but I think that was misguided.

I updated to make the calls in the writer & server constructors.

simply accept that we only have one string which is used for both C/C++

This is somewhat user-facing in that it ends up as the 'library' of mcap files, so I think picking either one of those could seem strange. Maybe there's some other name we can use to suggest 'either', but I think I prefer dealing with it at runtime.

The main drawback to me is having this C function around that doesn't have any use for end users.

@bryfox bryfox force-pushed the bryan/fg-10630-annotate-mcaps-websocket-connections-with-library-version branch from aa45717 to 779bf58 Compare March 21, 2025 19:05
@bryfox bryfox requested a review from gasmith March 21, 2025 20:38
@bryfox bryfox dismissed gasmith’s stale review March 21, 2025 20:38

c++ requires runtime changes

c/src/lib.rs Outdated
@@ -441,6 +441,12 @@ pub unsafe extern "C" fn foxglove_channel_log(
);
}

/// For use by the C++ SDK. Identifies that wrapper as the source of logs.
#[unsafe(no_mangle)]
pub extern "C" fn _register_cpp_wrapper() {
Copy link
Member

Choose a reason for hiding this comment

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

I think the leading underscore is technically not allowed in C/C++ https://devblogs.microsoft.com/oldnewthing/20230109-00/?p=107685 (Maybe trailing underscore is ok? or use "internal" in the name?)

I also would recommend including "foxglove" somewhere in the name

Comment on lines 94 to 95
env:
FOXGLOVE_SDK_LANGUAGE: cpp
Copy link
Member

Choose a reason for hiding this comment

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

maybe this can be removed now?

//! Provides an identifier for the library used as a log source.
use std::sync::OnceLock;

use crate::SDK_LANGUAGE;
Copy link
Member

Choose a reason for hiding this comment

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

nit: I find it a bit confusing that these two things are defined in different files, but they seem quite related

@@ -194,6 +198,12 @@ pub use sink::{Sink, SinkId};
pub(crate) use time::nanoseconds_since_epoch;
pub use websocket_server::{WebSocketServer, WebSocketServerBlockingHandle, WebSocketServerHandle};

pub(crate) static SDK_LANGUAGE: LazyLock<String> = LazyLock::new(|| {
Copy link
Member

Choose a reason for hiding this comment

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

consider naming this with "build"/"compile" somewhere in the name to indicate it was set at compile time?

Does this need to be LazyLock? It looks like it is read-only after creation

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 updated the name. LazyLock (or something like it) is needed because the unwrapping happens at runtime. I didn't see a way to define a default env var for the build, so would like to keep it optional.


/// Returns an identifer for this library, for use in log sinks.
pub(crate) fn get_library_version() -> String {
let language = CELL.get_or_init(|| SDK_LANGUAGE.as_str());
Copy link
Member

Choose a reason for hiding this comment

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

So I guess this means if get_library_version is called before set_sdk_language, then the latter will have no effect? That's probably fine, but wonder if it should be called out in the comments

@bryfox bryfox merged commit 428dc2c into main Apr 3, 2025
38 checks passed
@bryfox bryfox deleted the bryan/fg-10630-annotate-mcaps-websocket-connections-with-library-version branch April 3, 2025 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants