Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 46 additions & 26 deletions .cursor/skills/bump-libdatadog-version/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@ description: >-
Bump the pinned upstream libdatadog version for libdatadog-dotnet. Use when
the user says "Bump to the latest version of libdatadog", "Bump to libdatadog
vX.Y.Z", or otherwise asks to upgrade / update the libdatadog version in this
repo. Handles the LIBDATADOG_VERSION file and any required Rust toolchain pin
update.
repo. Handles the libdatadog git dependencies in datadog-dotnet-ffi/Cargo.toml
and any required Rust toolchain pin update.
disable-model-invocation: true
---

# Bump libdatadog Version

Upgrades the pinned upstream [libdatadog](https://github.com/DataDog/libdatadog)
version for this packaging repo. The version lives in a single file
(`LIBDATADOG_VERSION`, no `v` prefix), and a correct bump may also require a
Rust toolchain pin update.
version for this packaging repo. The version is the `tag` (e.g.
`tag = "v32.0.0"`) of all libdatadog git dependencies in
`datadog-dotnet-ffi/Cargo.toml` (a `rev = "<commit>"` while unreleased libdatadog
changes are used), and a correct bump may also require a Rust toolchain pin
update.

## Workflow

Copy this checklist and track progress:

```
- [ ] Step 1: Resolve the target version
- [ ] Step 2: Update LIBDATADOG_VERSION
- [ ] Step 2: Update the libdatadog dependencies in datadog-dotnet-ffi/Cargo.toml
- [ ] Step 3: Check upstream Rust MSRV; update the pin if changed
- [ ] Step 4: Check the macOS runner pin (only if MSRV/toolchain shifted)
- [ ] Step 5: Update AGENTS.md "Recent Changes" + README if needed
Expand All @@ -40,14 +42,29 @@ curl -fsSL https://api.github.com/repos/DataDog/libdatadog/releases/latest \
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"v?([^"]+)".*/\1/'
```

Read the current pin with the Read tool on `LIBDATADOG_VERSION`. If it already
equals the target, tell the user and stop. Otherwise **remember this value as
`OLD_VERSION`** — Step 2 overwrites the file, and Step 3 needs the old version
to detect an MSRV change.
Read the current pin (the `tag` or `rev` of the libdatadog dependencies) with
the Read tool on `datadog-dotnet-ffi/Cargo.toml`. If it already is
`tag = "v<TARGET>"`, tell the user and stop. Otherwise **remember the tag or
commit as `OLD_REF`** — Step 2 overwrites it, and Step 3 needs the old one to
detect an MSRV change.

### Step 2: Update LIBDATADOG_VERSION
### Step 2: Update the libdatadog dependencies

Write the bare version (no `v`, single trailing newline) to `LIBDATADOG_VERSION`.
Set `tag = "v<TARGET>"` (replacing `tag = ...` or `rev = ...`) on **every**
libdatadog git dependency in `datadog-dotnet-ffi/Cargo.toml` — they must all
use the same version — and update the comment above them.

Then update `Cargo.lock` (committed, the builds use `--locked`) to libdatadog's
dependency versions at that tag, which also makes sure it still builds:

```bash
cargo metadata --format-version 1 > /dev/null # resolves the new revision
cp "$(sh tools/libdatadog.sh dir)/Cargo.lock" Cargo.lock
cargo test -p datadog-dotnet-ffi # adds our own dependencies on top
```

Also check that the `libdd-profiling-ffi` feature list still matches
`builder/src/features.rs`.

### Step 3: Check upstream Rust MSRV; update the pin if changed

Expand All @@ -56,20 +73,20 @@ with a different toolchain than upstream can introduce subtle codegen / ABI
differences. The MSRV lives in `rust-version` under `[workspace.package]` in
libdatadog's root `Cargo.toml`.

Compare the MSRV at the **target** tag against the MSRV at the **`OLD_VERSION`**
tag (the value captured in Step 1) — don't compare against a hardcoded number,
since the repo's pin moves over time, and don't re-read `LIBDATADOG_VERSION`
Compare the MSRV at the **target** tag against the MSRV at **`OLD_REF`** (the
value captured in Step 1) — don't compare against a hardcoded number, since the
repo's pin moves over time, and don't re-read `datadog-dotnet-ffi/Cargo.toml`
here (Step 2 already overwrote it with `TARGET`, so it would falsely report no
change):

```bash
extract_msrv() { # $1 = libdatadog tag without leading v
curl -fsSL "https://raw.githubusercontent.com/DataDog/libdatadog/v$1/Cargo.toml" \
extract_msrv() { # $1 = libdatadog tag (with leading v) or commit
curl -fsSL "https://raw.githubusercontent.com/DataDog/libdatadog/$1/Cargo.toml" \
| grep -i 'rust-version' | head -n1 \
| sed -E 's/.*"([0-9]+\.[0-9]+(\.[0-9]+)?)".*/\1/'
}
echo "old: $(extract_msrv <OLD_VERSION>)" # the pin captured in Step 1
echo "target: $(extract_msrv <TARGET>)"
echo "old: $(extract_msrv <OLD_REF>)" # the pin captured in Step 1
echo "target: $(extract_msrv v<TARGET>)"
```

- **Same value** → MSRV unchanged, leave the toolchain alone. Skip to Step 4.
Expand All @@ -88,8 +105,9 @@ Then update **all** of these from the old MSRV to the target MSRV:
- `tools/docker/Dockerfile.musl-x64` — `ARG RUST_VERSION`
- `tools/docker/Dockerfile.musl-aarch64` — `ARG RUST_VERSION`
- `README.md` — the prerequisites line
- `datadog-dotnet-ffi/Cargo.toml` — `rust-version`

Keep this in the **same commit** as the `LIBDATADOG_VERSION` bump — the two are
Keep this in the **same commit** as the libdatadog bump — the two are
coupled (building the new version with the old toolchain is exactly the
mismatch the pin guards against). The change is picked up automatically on the
next build; there are no prebuilt/pushed images to refresh:
Expand All @@ -115,8 +133,9 @@ revisit it if upstream moved its macOS build image; the `os:` fields live in
- Add a dated entry to the top of the **Recent Changes** list in `AGENTS.md`
(e.g. `- **YYYY-MM**: Bumped to libdatadog v<TARGET>.` and note the MSRV change
if any).
- The README's libdatadog references are version-agnostic (they read the file),
so only edit it if the Rust pin changed (Step 3).
- The README's libdatadog references are version-agnostic (they point to
`datadog-dotnet-ffi/Cargo.toml`), so only edit it if the Rust pin changed
(Step 3).

### Step 6: Summarize and propose next steps

Expand All @@ -128,19 +147,20 @@ Report what changed (version, and MSRV pin if touched). Then offer to:
Do not commit or open a PR unless the user asks.

**Always flag the coordinated-rollout caveat** so the bump isn't mistaken for a
complete, self-contained change: bumping `LIBDATADOG_VERSION` (and cutting a
complete, self-contained change: bumping libdatadog (and cutting a
release) here is fine on its own, but libdatadog's profiling FFI API drifts
between tags. The new version is only proven compatible once the dd-trace-dotnet
PR that adopts the new libdatadog-dotnet **release tag** (e.g. `v1.3.5` — a
different number from `LIBDATADOG_VERSION`) compiles its native profiler
different number from the libdatadog version) compiles its native profiler
(`cor_profiler.cpp`, `dd_profiler_constants.h`) against the matching profiling
headers in the same rollout. This is a coordination requirement on the tracer
side, **not** a precondition that blocks the bump here.

## Notes

- Feature set is **not** part of a version bump — leave `FEATURES` / `Features`
/ workflow `features` defaults alone unless the user explicitly asks.
- Feature set is **not** part of a version bump — leave `FEATURES` / `$Features`
(and the matching `libdd-profiling-ffi` features) alone unless the user
explicitly asks.
- The `curl` commands need network access but no auth or extra tooling (they
hit public release/raw endpoints). If `curl` is unavailable, `wget -qO-` works
with the same URLs.
11 changes: 3 additions & 8 deletions .github/workflows/build-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ name: Build Platform
on:
workflow_call:
inputs:
features:
description: 'Comma-separated builder crate features (e.g. profiling,crashtracker,symbolizer,library-config)'
required: false
type: string
default: 'profiling,crashtracker,symbolizer,library-config'
verify_artifacts:
description: 'Whether to verify build artifacts after building'
required: false
Expand Down Expand Up @@ -91,7 +86,7 @@ jobs:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('LIBDATADOG_VERSION') }}
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-

Expand All @@ -109,10 +104,10 @@ jobs:
CARGO_BUILD_TARGET: ${{ matrix.target }}
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "./build.ps1 -Platform '${{ matrix.platform }}' -Features '${{ inputs.features }}' -Clean"
pwsh -Command "./build.ps1 -Platform '${{ matrix.platform }}' -Clean"
else
chmod +x build.sh
./build.sh --platform "${{ matrix.platform }}" --features "${{ inputs.features }}" --clean
./build.sh --platform "${{ matrix.platform }}" --clean
fi
shell: bash

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,3 @@ jobs:
contents: read
with:
verify_artifacts: true
features: 'profiling,crashtracker,symbolizer,library-config'
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ jobs:
contents: read
with:
verify_artifacts: false
features: 'profiling,crashtracker,symbolizer,library-config'

create-release:
name: Create GitHub Release
Expand Down Expand Up @@ -211,7 +210,7 @@ jobs:
cat > release-notes.md << 'NOTES_EOF'
## Build Information

- **Libdatadog version**: see `LIBDATADOG_VERSION` in this repository
- **Libdatadog version**: see the libdatadog dependencies in `datadog-dotnet-ffi/Cargo.toml` in this repository

## Checksums

Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Rust
target/
Cargo.lock
**/*.rs.bk
*.pdb

Expand Down
29 changes: 20 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,32 @@ A thin packaging wrapper around the official [libdatadog](https://github.com/Dat

## Build Approach

We **don't** orchestrate cargo + cbindgen ourselves. We install libdatadog's own `release` binary (from its `builder` crate) and let it produce the package:
We **don't** orchestrate cbindgen ourselves. We install libdatadog's own `release` binary (from its `builder` crate) from the sources `datadog-dotnet-ffi` is built from — cargo's checkout of its libdatadog dependencies, found by `tools/libdatadog.sh` via `cargo metadata` — and let it produce the package, then replace the package's shared library with the `datadog-dotnet-ffi` build:

```
cargo install \
--git https://github.com/DataDog/libdatadog \
--tag "v${LIBDATADOG_VERSION}" \
--path "$(tools/libdatadog.sh dir)/builder" \
--bin release \
--no-default-features \
--features "profiling,crashtracker,symbolizer,library-config" \
builder
release --out <package-dir> --target <triple>
tools/build-ffi.sh <triple> <profile> <package-dir>
```

The builder handles the FFI cargo build, header generation (cbindgen + dedup), library stripping, and packaging. We just orchestrate per-platform Docker containers and (on Windows) reshape the resulting zip to match what dd-trace-dotnet expects.
The builder handles the FFI cargo build, header generation (cbindgen + dedup), library stripping, and packaging. We orchestrate per-platform Docker containers and (on Windows) reshape the resulting zip to match what dd-trace-dotnet expects.

`datadog-dotnet-ffi` (the only Rust code here) is a cdylib that depends on libdatadog's crates from git (see [Version Pinning](#version-pinning)) and re-exports `libdd-profiling-ffi` (with the feature list the builder computes for our builder features — keep `datadog-dotnet-ffi/Cargo.toml` in sync with `builder/src/features.rs`), plus the .NET specific FFI (`src/remote_config.rs`: the remote configuration client used by dd-trace-dotnet). Its library is named `datadog_dotnet_ffi` (the dependency's own cdylib would collide otherwise), and `tools/build-ffi.sh` / `build.ps1` copy it over the builder's library under the original names (setting the soname / install name, and on Windows generating an import library for `datadog_profiling_ffi.dll`). Its dependencies are locked by the committed `Cargo.lock` (built with `--locked`), which starts from libdatadog's `Cargo.lock` at the pinned revision, see Common Tasks.

Use `--libdatadog-path ../libdatadog` (`-LibdatadogPath`) to build against a local libdatadog checkout, e.g. to test libdatadog changes: the build scripts patch the libdatadog dependencies of `datadog-dotnet-ffi` to it (a `[patch]` in `target/libdatadog-patch.toml`, passed to cargo with `--config` via `LIBDATADOG_PATCH`), so the builder comes from there too. It is mounted at the same path into the build containers. The patch changes `Cargo.lock` locally; don't commit that.

## Version Pinning

`LIBDATADOG_VERSION` (single file, no `v` prefix, e.g. `32.0.0`) is the **upstream** libdatadog version this repo builds from. It is distinct from libdatadog-dotnet's own release tag (e.g. `v1.3.5`) that dd-trace-dotnet pins — don't expect the two numbers to be equal.
The `tag` (e.g. `tag = "v32.0.0"`) of the libdatadog git dependencies in `datadog-dotnet-ffi/Cargo.toml` is the **upstream** libdatadog version this repo builds from — all of them must use the same one. To depend on unreleased libdatadog changes, use `rev = "<commit>"` instead, and go back to a tag once they are released. The builder comes from the same sources, so the headers always match the library; the package version passed to it (pkg-config) is the libdatadog workspace version of those sources. It is distinct from libdatadog-dotnet's own release tag (e.g. `v1.3.5`) that dd-trace-dotnet pins — don't expect the two numbers to be equal.

⚠️ **The chosen libdatadog version must be API-compatible with dd-trace-dotnet's native profiler C++.** The tracer's profiler (`cor_profiler.cpp`, `dd_profiler_constants.h`) calls libdatadog's profiling FFI (`ddog_prof_SampleType`, `DDOG_PROF_SAMPLE_TYPE_*`, …), and that API drifts between libdatadog tags. A mismatch surfaces in the tracer build as cryptic C++ errors like `error C2061: 'ddog_prof_SampleType'`.

This is a **coordinated** change, not a precondition. You *can* bump `LIBDATADOG_VERSION` and cut a release ahead of the tracer — the tracer won't reference the new release yet, and that's expected. The requirement is only that the dd-trace-dotnet PR which adopts the new libdatadog-dotnet release also builds against its profiling headers in the same rollout, so the native profiler compiles against the matching API.
This is a **coordinated** change, not a precondition. You *can* bump the libdatadog version and cut a release ahead of the tracer — the tracer won't reference the new release yet, and that's expected. The requirement is only that the dd-trace-dotnet PR which adopts the new libdatadog-dotnet release also builds against its profiling headers in the same rollout, so the native profiler compiles against the matching API.

## Per-Platform Build Setup

Expand Down Expand Up @@ -97,9 +101,13 @@ So `build.ps1`:

```
libdatadog-dotnet/
├── LIBDATADOG_VERSION # Pinned upstream libdatadog tag (no "v")
├── Cargo.toml # Workspace (release profile = libdatadog's)
├── datadog-dotnet-ffi/ # The shipped library: profiling FFI + .NET specific FFI;
│ # its Cargo.toml pins the upstream libdatadog version
├── build.sh # Linux/macOS — docker orchestration + macOS native
├── build.ps1 # Windows — runs builder twice, re-assembles legacy zip
├── tools/build-ffi.sh # Builds datadog-dotnet-ffi into a builder package
├── tools/libdatadog.sh # Locates the libdatadog sources (for the builder)
├── tools/docker/
│ ├── Dockerfile.centos # x86_64-gnu: cross-rs centos7 + devtoolset-10
│ ├── Dockerfile.centos-aarch64 # aarch64-gnu: manylinux2014_aarch64 + rustup
Expand All @@ -113,9 +121,9 @@ libdatadog-dotnet/

## Common Tasks

**Update libdatadog version**: edit `LIBDATADOG_VERSION` (the upstream libdatadog version this repo builds from). Ensure the target libdatadog's profiling FFI is API-compatible with dd-trace-dotnet's native profiler C++ — the tracer adopts the resulting libdatadog-dotnet release in a coordinated PR; it won't already reference it. See [Version Pinning](#version-pinning).
**Update libdatadog version**: change the `tag` of the libdatadog dependencies in `datadog-dotnet-ffi/Cargo.toml` (the upstream libdatadog version this repo builds from), and update `Cargo.lock` to libdatadog's dependency versions at that tag: `cargo metadata --format-version 1 > /dev/null` (resolves the new revision), `cp "$(sh tools/libdatadog.sh dir)/Cargo.lock" Cargo.lock`, then `cargo test -p datadog-dotnet-ffi` (adds our own dependencies on top). Ensure the target libdatadog's profiling FFI is API-compatible with dd-trace-dotnet's native profiler C++ — the tracer adopts the resulting libdatadog-dotnet release in a coordinated PR; it won't already reference it. See [Version Pinning](#version-pinning).

**Change feature set**: edit the `FEATURES` default in `build.sh`, the `Features` default in `build.ps1`, and the `features` input default in `build-platform.yml` / `release.yml` / `build.yml`. Feature names are the builder crate's high-level features (`profiling`, `crashtracker`, `data-pipeline`, `symbolizer`, `library-config`, `log`, `telemetry`, `ddsketch`, `ffe`, `otel-thread-ctx`, `shared-runtime`, `regex-lite`), not the underlying cargo features — e.g. the builder's `otel-thread-ctx` feature internally maps to the `otel-thread-ctx-ffi` FFI crate (see `builder/src/bin/release.rs` in libdatadog), so pass `otel-thread-ctx` here, never the FFI crate name.
**Change feature set**: edit `FEATURES` in `build.sh` and `$Features` in `build.ps1` (the builder features, which determine the headers etc.), together with the `libdd-profiling-ffi` features in `datadog-dotnet-ffi/Cargo.toml` (what the builder computes for them, see `builder/src/features.rs`), which the shipped library is built with. Feature names are the builder crate's high-level features (`profiling`, `crashtracker`, `data-pipeline`, `symbolizer`, `library-config`, `log`, `telemetry`, `ddsketch`, `ffe`, `otel-thread-ctx`, `shared-runtime`, `regex-lite`), not the underlying cargo features — e.g. the builder's `otel-thread-ctx` feature internally maps to the `otel-thread-ctx-ffi` FFI crate (see `builder/src/bin/release.rs` in libdatadog), so pass `otel-thread-ctx` here, never the FFI crate name.

**Add a platform**: matrix entry in `build-platform.yml`; if it's not host-native, either add a Dockerfile + dispatch case in `build.sh`, or use QEMU. Update the release archive list in `release.yml`.

Expand Down Expand Up @@ -144,6 +152,9 @@ Output lands in `output/libdatadog-<platform>/`. For aarch64 Linux targets you n

## Recent Changes (Reverse Chronological)

- **2026-10**: Added `datadog-dotnet-ffi`, which replaces the builder's shared library, with the remote configuration client FFI (`ddog_rc_*`) for dd-trace-dotnet. The libdatadog version is now pinned by its git dependencies (replacing the `LIBDATADOG_VERSION` file), currently v45 plus the unreleased remote config changes of DataDog/libdatadog#2662; the builder is installed from cargo's checkout of them (`tools/libdatadog.sh`) instead of `cargo install --git --tag`. `Cargo.lock` is committed, and the feature set is fixed (no more `--features` / workflow input), as the shipped library is built with the features in `datadog-dotnet-ffi/Cargo.toml`.

- **2026-10**: Bumped to libdatadog v45 (from v38). Rust toolchain pin still `1.87.0` (v45's MSRV). The profiling, crashtracker and library-config headers are source compatible with v38 (only additions), so dd-trace-dotnet's native profiler builds unchanged; the musl builds import a few more libc symbols.
- **2026-07**: Bumped to libdatadog v38 (from v36). Rust toolchain pin already at `1.87.0`, which matches v38's MSRV, so no toolchain change was needed.
- **2026-06**: Bumped to libdatadog v36 (from v32). Rust toolchain pin already at `1.87.0`, which matches v36's MSRV, so no toolchain change was needed.
- **2026-05**: aarch64-gnu switched from cross-rs cross-compile to QEMU + native manylinux2014_aarch64. Necessary because libdd-libunwind-sys (v30) can't be cross-compiled.
Expand Down
Loading
Loading