Skip to content
Merged
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
13 changes: 10 additions & 3 deletions .github/workflows/build-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ on:
required: false
type: boolean
default: false
file_version:
description: 'libdatadog-dotnet release version (major.minor.patch) embedded as the Windows DLL FileVersion via release --file-version. Empty omits the flag, so FileVersion falls back to the upstream libdatadog version. Has no effect on non-Windows targets.'
required: false
type: string
default: ''

permissions:
contents: read
Expand Down Expand Up @@ -74,8 +79,8 @@ jobs:
# subtle codegen / inlining differences between releases.
- name: Setup Rust
run: |
rustup install 1.87.0
rustup default 1.87.0
rustup install 1.91.1
rustup default 1.91.1

- name: Add Windows target
if: runner.os == 'Windows'
Expand Down Expand Up @@ -109,7 +114,9 @@ jobs:
CARGO_BUILD_TARGET: ${{ matrix.target }}
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "./build.ps1 -Platform '${{ matrix.platform }}' -Features '${{ inputs.features }}' -Clean"
# -FileVersion is this repo's release (e.g. 1.3.5), not LIBDATADOG_VERSION.
# Empty on PR builds; the Release workflow always sets it.
pwsh -Command "./build.ps1 -Platform '${{ matrix.platform }}' -Features '${{ inputs.features }}' -FileVersion '${{ inputs.file_version }}' -Clean"
else
chmod +x build.sh
./build.sh --platform "${{ matrix.platform }}" --features "${{ inputs.features }}" --clean
Expand Down
55 changes: 38 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,16 @@ permissions:
contents: read

jobs:
build:
name: Build all platforms
uses: ./.github/workflows/build-platform.yml
permissions:
contents: read
with:
verify_artifacts: false
features: 'profiling,crashtracker,symbolizer,library-config'

create-release:
name: Create GitHub Release
needs: build
# Resolve the libdatadog-dotnet release tag before the build so Windows
# DLLs can embed it as FileVersion. This is this repo's own tag (v1.3.5),
# not LIBDATADOG_VERSION — Windows Installer compares FileVersion when
# deciding whether to replace the DLL on upgrade.
resolve-version:
name: Resolve release version
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # OIDC — required for dd-octo-sts token minting

outputs:
release_tag: ${{ steps.setup_release.outputs.RELEASE_TAG }}
file_version: ${{ steps.setup_release.outputs.FILE_VERSION }}
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -106,8 +99,36 @@ jobs:
exit 1
fi

# major.minor.patch, no leading v — what `release --file-version` expects.
FILE_VERSION="${RELEASE_TAG#v}"

echo "RELEASE_TAG=$RELEASE_TAG" >> $GITHUB_OUTPUT
echo "FILE_VERSION=$FILE_VERSION" >> $GITHUB_OUTPUT
echo "Release tag: $RELEASE_TAG"
echo "Windows FileVersion: $FILE_VERSION"

build:
name: Build all platforms
needs: resolve-version
uses: ./.github/workflows/build-platform.yml
permissions:
contents: read
with:
verify_artifacts: false
features: 'profiling,crashtracker,symbolizer,library-config'
file_version: ${{ needs.resolve-version.outputs.file_version }}

create-release:
name: Create GitHub Release
needs: [build, resolve-version]
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write # OIDC — required for dd-octo-sts token minting

steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
Expand Down Expand Up @@ -259,7 +280,7 @@ jobs:
- name: Create or Update Release
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
RELEASE_TAG: ${{ steps.setup_release.outputs.RELEASE_TAG }}
RELEASE_TAG: ${{ needs.resolve-version.outputs.release_tag }}
with:
# octo-sts token (see step above) has ruleset-bypass rights, so
# createRelease() can create the tag when it does not yet exist.
Expand Down
18 changes: 11 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ cargo install \
--no-default-features \
--features "profiling,crashtracker,symbolizer,library-config" \
builder
release --out <package-dir> --target <triple>
release --out <package-dir> --target <triple> [--file-version <major.minor.patch>]
```

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.

`--file-version` is Windows-only. It must be **this repo's release version** (`1.3.5`, from tag `v1.3.5`) — not `LIBDATADOG_VERSION`. The builder forwards it as `LIBDATADOG_PACKAGE_VERSION` and embeds it as the DLL `FileVersion`, which Windows Installer compares on upgrade. `ProductVersion` stays the upstream libdatadog version. Omit the flag and `FileVersion` falls back to that upstream version. `build.ps1 -FileVersion` is the local entry point; the Release workflow passes it automatically. PR builds leave it empty.

## 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.
Expand Down Expand Up @@ -61,9 +63,9 @@ include/datadog/… cmake/DatadogConfig.cmake LICENSE NOTICE LICENSE-3rdpart
```

So `build.ps1`:
1. Runs the builder twice — once with `PROFILE=release`, once with `PROFILE=debug`.
1. Runs the builder twice — once with `PROFILE=release`, once with `PROFILE=debug`. When `-FileVersion` is set, each invocation gets `--file-version`, which the builder forwards to cargo as `LIBDATADOG_PACKAGE_VERSION` so the DLL's `VERSIONINFO` `FileVersion` is the libdatadog-dotnet release (not `LIBDATADOG_VERSION`).
2. Pulls headers / license / cmake from the release-builder output.
3. Pulls `.dll`, `.dll.lib`, `.lib` (static), `.pdb` **directly from `target/$Target/{release,debug}/`** because cargo there has the original `_ffi` names AND the `.dll.lib` import library that the builder doesn't copy.
3. Pulls `.dll`, `.dll.lib`, `.lib` (static), `.pdb` **directly from `target/$Target/{release,debug}/`** because cargo there has the original `_ffi` names AND the `.dll.lib` import library that the builder doesn't copy. The `FileVersion` resource is already in that DLL.
4. Renames `.dll.lib` → `.lib` in each `dynamic/` slot.

## Gotchas
Expand All @@ -78,7 +80,7 @@ So `build.ps1`:

5. **cmake-rs needs cargo build-script env on macOS**: the builder calls `cmake::Config::build()` for the crashtracker C++ receiver outside a cargo build-script context. cmake-rs / cc-rs read `HOST` / `OUT_DIR` / `OPT_LEVEL` / `DEBUG` / `NUM_JOBS`. The macOS branch of `build.sh` sets all of them; missing any one panics with `environment variable 'X' not defined`. (Windows doesn't hit this — `BUILD_CRASHTRACKER = false` in the builder's `arch/windows.rs`.)

6. **Toolchain pinned to upstream libdatadog's**: Rust `1.87.0` (matches libdatadog's workspace `rust-version`) and macOS runners `macos-14` / `macos-14-large` (Sonoma, matches libdatadog's libddprof-build pipeline). When upstream libdatadog bumps its MSRV or its macOS build version, update both here — building with a different toolchain than upstream can introduce subtle codegen / ABI differences. The pin lives in: `build-platform.yml` (Setup Rust step + matrix `os:` fields), `Dockerfile.centos-aarch64` (rustup-init `--default-toolchain`), and `Dockerfile.musl-*` (`ARG RUST_VERSION` consumed by the rustup install).
6. **Toolchain pinned to upstream libdatadog's**: Rust `1.91.1` (matches libdatadog's workspace `rust-version`) and macOS runners `macos-14` / `macos-14-large` (Sonoma, matches libdatadog's libddprof-build pipeline). When upstream libdatadog bumps its MSRV or its macOS build version, update both here — building with a different toolchain than upstream can introduce subtle codegen / ABI differences. The pin lives in: `build-platform.yml` (Setup Rust step + matrix `os:` fields), `Dockerfile.centos-aarch64` (rustup-init `--default-toolchain`), and `Dockerfile.musl-*` (`ARG RUST_VERSION` consumed by the rustup install).

7. **Release workflow auth (dd-octo-sts, keyless)**: the repo's ref-creation ruleset blocks `GITHUB_TOKEN` from creating new tags. `release.yml` mints a short-lived token via **DataDog's octo-sts** (OIDC federation — no stored secret) in the `octo-sts` step, and the `actions/github-script` publish step authenticates with `github-token: ${{ steps.octo-sts.outputs.token }}`. The octo-sts identity is on the ruleset's **bypass list**, so it can create the tag ref. This matches how **dd-trace-dotnet and upstream libdatadog** authenticate their release workflows — and because it's OIDC-federated there's **no token to store or rotate** (this replaced the old `RELEASE_TOKEN` PAT, which DataDog policy capped at ~30 days, forcing monthly rotation).

Expand Down Expand Up @@ -125,16 +127,16 @@ libdatadog-dotnet/

- **`build-platform.yml`** — reusable matrix workflow. Steps: checkout → setup Rust → cache `~/.cargo/{registry,git}` → setup QEMU (only for aarch64 Linux targets) → run `build.{sh,ps1}` → verify output → upload artifact.
- **`build.yml`** — calls `build-platform.yml` on PR / push to main with `verify_artifacts: true`.
- **`release.yml`** — manual dispatch (from `main`). Builds all 8 platforms (no verify), archives them, computes SHA256 + SHA512, creates a GitHub release with assets and a checksums-in-the-body note. Publish step auth via dd-octo-sts (OIDC; policy in `.github/chainguard/self.release.sts.yaml`).
- **`release.yml`** — manual dispatch (from `main`). Resolves this repo's release tag first, passes it to the Windows build as `file_version` (`release --file-version`, the DLL `FileVersion`), builds all 8 platforms (no verify), archives them, computes SHA256 + SHA512, creates a GitHub release with assets and a checksums-in-the-body note. Publish step auth via dd-octo-sts (OIDC; policy in `.github/chainguard/self.release.sts.yaml`).

## Testing Locally

```bash
# Linux GNU (x86_64), full clean build
./build.sh --platform x86_64-unknown-linux-gnu --clean

# Windows
./build.ps1 -Platform x64-windows -Clean
# Windows. -FileVersion is the libdatadog-dotnet release, not LIBDATADOG_VERSION.
./build.ps1 -Platform x64-windows -Clean -FileVersion 1.3.5

# macOS
./build.sh --platform aarch64-apple-darwin --clean
Expand All @@ -144,6 +146,8 @@ Output lands in `output/libdatadog-<platform>/`. For aarch64 Linux targets you n

## Recent Changes (Reverse Chronological)

- **2026-10**: Windows release builds pass `release --file-version` set to the libdatadog-dotnet release (not `LIBDATADOG_VERSION`), so the DLL `FileVersion` is what Windows Installer compares on upgrade. `ProductVersion` stays the upstream libdatadog version.
- **2026-10**: Bumped to libdatadog v47 (from v38). Rust toolchain pin moved from `1.87.0` to `1.91.1` to match v47's MSRV. macOS runners stay on `macos-14` / `macos-14-large` (Sonoma); libddprof-build still builds on the Sonoma AMI.
- **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
2 changes: 1 addition & 1 deletion LIBDATADOG_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
38.0.0
47.0.0
Comment thread
andrewlock marked this conversation as resolved.
Resolved
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A thin packaging layer over upstream libdatadog. Ships only the FFI components t

### Prerequisites

- Rust 1.87.0 or newer (CI pins to 1.87.0 to match libdatadog's MSRV)
- Rust 1.91.1 or newer (CI pins to 1.91.1 to match libdatadog's MSRV)
- Git
- PowerShell on Windows; Bash + Docker (with buildx and QEMU for aarch64 Linux builds) elsewhere

Expand All @@ -23,8 +23,9 @@ A thin packaging layer over upstream libdatadog. Ships only the FFI components t
# macOS arm64
./build.sh --platform aarch64-apple-darwin --clean

# Windows
./build.ps1 -Platform x64-windows -Clean
# Windows. -FileVersion is this repo's release (major.minor.patch), embedded
# as the DLL FileVersion. Omit it for a local build that isn't a release.
./build.ps1 -Platform x64-windows -Clean -FileVersion 1.3.5
```

Output lands in `output/libdatadog-<platform>/`.
Expand Down Expand Up @@ -53,12 +54,14 @@ cargo install \
--no-default-features \
--features "<feature-list>" \
builder
release --out <package-dir> --target <triple>
release --out <package-dir> --target <triple> [--file-version <major.minor.patch>]
```

The builder handles the FFI cargo build, header generation (cbindgen + dedup), library stripping, and packaging.

**Windows quirk**: the builder produces a flat `lib/datadog_profiling.{dll,lib,pdb}` layout, but dd-trace-dotnet's vcpkg portfile expects the legacy `release/{dynamic,static}` + `debug/{dynamic,static}` tree with `_ffi`-suffixed filenames. `build.ps1` runs the builder twice (release + debug) and reassembles that layout from cargo's raw `target/` output.
**Windows `FileVersion`**: `--file-version` is the libdatadog-dotnet release (`1.3.5` from tag `v1.3.5`), not `LIBDATADOG_VERSION`. The builder forwards it as `LIBDATADOG_PACKAGE_VERSION` and embeds it as the DLL `FileVersion` — the field Windows Installer compares when deciding whether to replace the file on upgrade. `ProductVersion` stays the upstream libdatadog version, so that pin is still recoverable from the shipped DLL. The flag does nothing on non-Windows targets. The Release workflow passes it automatically; omit it and `FileVersion` falls back to the upstream libdatadog version.

**Windows quirk**: the builder produces a flat `lib/datadog_profiling.{dll,lib,pdb}` layout, but dd-trace-dotnet's vcpkg portfile expects the legacy `release/{dynamic,static}` + `debug/{dynamic,static}` tree with `_ffi`-suffixed filenames. `build.ps1` runs the builder twice (release + debug) and reassembles that layout from cargo's raw `target/` output. The `VERSIONINFO` resource is embedded during that cargo build, so the reassembled DLL keeps the `FileVersion` set by `--file-version`.

## GLIBC Compatibility

Expand All @@ -82,7 +85,7 @@ Manual dispatch: **Actions → Release → Run workflow**. The workflow builds a
| `version_increment` | `patch` | `patch` / `minor` / `major` — ignored if `release_version` is set |
| `release_version` | _(empty)_ | Manual override, e.g. `v1.4.0` |

The upstream libdatadog version isn't a workflow input — it's pinned in the `LIBDATADOG_VERSION` file at the time of the release commit.
The upstream libdatadog version isn't a workflow input — it's pinned in the `LIBDATADOG_VERSION` file at the time of the release commit. The workflow resolves this repo's own release tag first and passes it to the Windows build as `--file-version`, so the DLL `FileVersion` matches the release (for example `1.4.0`) rather than `LIBDATADOG_VERSION`.

For CI runs without publishing a release, use the **Build** workflow (auto-triggered on every PR and push to `main`).

Expand Down
39 changes: 33 additions & 6 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ param(
[string]$Platform = "x64-windows",
[string]$OutputDir = "output",
[string]$Features = "profiling,crashtracker,symbolizer,library-config",
# libdatadog-dotnet release version (major.minor.patch, optional leading "v"),
# embedded as the DLL FileVersion. Distinct from LIBDATADOG_VERSION: Windows
# Installer compares FileVersion when deciding whether to replace the file
# on upgrade, so it has to change with this repo's release, not with the
# upstream libdatadog pin. Empty omits --file-version and the builder falls
# back to the upstream version. Ignored by the builder on non-Windows targets.
[string]$FileVersion = "",
[switch]$Clean
)

Expand All @@ -50,12 +57,24 @@ $Target = if ($env:CARGO_BUILD_TARGET) {
# Read the libdatadog version. Update LIBDATADOG_VERSION to upgrade.
$Version = (Get-Content LIBDATADOG_VERSION).Trim()

# --file-version wants major.minor.patch. Accept a leading "v" so callers can
# pass the release tag (v1.3.5) directly.
$NormalizedFileVersion = ""
if ($FileVersion) {
$NormalizedFileVersion = $FileVersion -replace '^v', ''
if ($NormalizedFileVersion -notmatch '^\d+\.\d+\.\d+$') {
Write-Host "Error: -FileVersion must be major.minor.patch (got '$FileVersion'). This is the libdatadog-dotnet release version, not LIBDATADOG_VERSION." -ForegroundColor Red
exit 1
}
}

Write-Host "Building libdatadog-dotnet" -ForegroundColor Cyan
Write-Host " Platform : $Platform" -ForegroundColor Gray
Write-Host " Target : $Target" -ForegroundColor Gray
Write-Host " Features : $Features" -ForegroundColor Gray
Write-Host " Output : $OutputDir" -ForegroundColor Gray
Write-Host " Version : $Version" -ForegroundColor Gray
Write-Host " Platform : $Platform" -ForegroundColor Gray
Write-Host " Target : $Target" -ForegroundColor Gray
Write-Host " Features : $Features" -ForegroundColor Gray
Write-Host " Output : $OutputDir" -ForegroundColor Gray
Write-Host " Libdatadog : $Version" -ForegroundColor Gray
Write-Host " FileVersion : $(if ($NormalizedFileVersion) { $NormalizedFileVersion } else { '(upstream libdatadog version)' })" -ForegroundColor Gray

if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
Write-Host "Error: cargo not found. Install Rust from https://rustup.rs/" -ForegroundColor Red
Expand Down Expand Up @@ -126,8 +145,16 @@ function Invoke-Builder {
# is bulletproof.
$stdoutFile = Join-Path $OutputDir "_builder-$BuildProfile.stdout.log"
$stderrFile = Join-Path $OutputDir "_builder-$BuildProfile.stderr.log"
# --file-version is forwarded to the cargo build as
# LIBDATADOG_PACKAGE_VERSION and embedded in the DLL VERSIONINFO.
# ProductVersion stays the upstream libdatadog version.
$releaseArgs = @("--out", $BuilderOut, "--target", $Target)
if ($NormalizedFileVersion) {
$releaseArgs += @("--file-version", $NormalizedFileVersion)
}

$proc = Start-Process -FilePath ".\.builder\bin\release.exe" `
-ArgumentList "--out", "$BuilderOut", "--target", "$Target" `
-ArgumentList $releaseArgs `
-NoNewWindow -PassThru -Wait `
-RedirectStandardOutput $stdoutFile `
-RedirectStandardError $stderrFile
Expand Down
2 changes: 1 addition & 1 deletion tools/docker/Dockerfile.centos-aarch64
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ FROM quay.io/pypa/manylinux2014_aarch64
ENV CARGO_HOME=/root/.cargo \
RUSTUP_HOME=/root/.rustup
RUN curl -fsSL https://sh.rustup.rs -o /tmp/rustup-init.sh \
&& sh /tmp/rustup-init.sh -y --default-toolchain 1.87.0 --profile minimal --no-modify-path \
&& sh /tmp/rustup-init.sh -y --default-toolchain 1.91.1 --profile minimal --no-modify-path \
&& rm /tmp/rustup-init.sh
ENV PATH="/root/.cargo/bin:${PATH}"

Expand Down
2 changes: 1 addition & 1 deletion tools/docker/Dockerfile.musl-aarch64
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH

ARG RUST_VERSION=1.87.0
ARG RUST_VERSION=1.91.1
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- --default-toolchain ${RUST_VERSION} --profile minimal -y --no-modify-path \
&& rustc --version \
Expand Down
2 changes: 1 addition & 1 deletion tools/docker/Dockerfile.musl-x64
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH

ARG RUST_VERSION=1.87.0
ARG RUST_VERSION=1.91.1
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- --default-toolchain ${RUST_VERSION} --profile minimal -y --no-modify-path \
&& rustc --version \
Expand Down
Loading