From e21bad8c042d2384d487e6b7a4c40b797181197c Mon Sep 17 00:00:00 2001 From: Julio Date: Mon, 8 Jun 2026 13:27:33 +0200 Subject: [PATCH 1/2] chore: add support to build libdatadog from a commit reference --- README.md | 27 ++++++++++++++++++++ tasks/build.rake | 64 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 993de02..b0d464b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,33 @@ Otherwise, this is possibly not the droid you were looking for. Run `bundle exec rake` to run the tests and the style autofixer. You can also run `bundle exec pry` for an interactive prompt that will allow you to experiment. +### Building from source + +`bundle exec rake libdatadog:build` builds libdatadog from source for the current platform +using libdatadog's own [`builder` crate](https://github.com/DataDog/libdatadog/tree/main/builder). +It requires a Rust toolchain (plus `cmake` and autotools); the Nix dev shell provides all of these. +Artifacts are written to `vendor/libdatadog-//`. + +By default it builds the pinned `v` tag. The following environment variables +select what gets built. They are optional and **mutually exclusive** — set at most one +(setting more than one fails): + +- `LIBDATADOG_TAG` — build a specific git tag, e.g. `LIBDATADOG_TAG=v33.0.0`. +- `LIBDATADOG_COMMIT` — build a specific commit, e.g. `LIBDATADOG_COMMIT=`. +- `LIBDATADOG_SOURCE` — build from a local libdatadog checkout, e.g. `LIBDATADOG_SOURCE=/path/to/libdatadog`. + To build a branch, check it out locally and point this at the checkout. + +Independently of the above, `LIBDATADOG_FEATURES` sets a comma-separated cargo feature +override and applies to any build. + +Tag and commit builds pass `cargo install --locked`, so they reproducibly use the dependency +versions pinned in libdatadog's `Cargo.lock`. Local-source builds omit `--locked`, since the +checkout may be modified. + +```sh +LIBDATADOG_COMMIT= bundle exec rake libdatadog:build +``` + ### Testing packaging locally You can use `bundle exec rake package` to generate packages locally without publishing them. diff --git a/tasks/build.rake b/tasks/build.rake index dfe1300..f0cb670 100644 --- a/tasks/build.rake +++ b/tasks/build.rake @@ -81,22 +81,46 @@ module BuildFromSource end module Builder + LIBDATADOG_GIT_URL = "https://github.com/DataDog/libdatadog" + class << self # Build the cargo install command for the builder crate's `release` binary. # - # source: optional path to a local libdatadog checkout - # features: optional comma-separated feature override - def cargo_install_cmd(source: nil, features: nil) + # The libdatadog code to build is selected from exactly one of the following, + # which are mutually exclusive (passing more than one raises): + # source: path to a local libdatadog checkout. Built via --path, WITHOUT + # --locked, since the checkout may be modified locally. + # tag: a git tag. Built via --git --tag --locked. + # ref: a git commit. Built via --git --rev --locked. + # When none are given, defaults to the pinned --tag v --locked. + # + # Git builds pass --locked so they reproducibly use libdatadog's Cargo.lock. + # features: optional comma-separated cargo feature override, appended in all cases. + def cargo_install_cmd(source: nil, tag: nil, ref: nil, features: nil) + source = presence(source) + tag = presence(tag) + ref = presence(ref) + features = presence(features) + + selected = {source: source, tag: tag, ref: ref}.select { |_, value| value } + if selected.size > 1 + raise "Only one of source, tag, ref may be set at a time (got: #{selected.keys.join(", ")})" + end + cmd = %W[cargo install --bin release --root #{Paths.builder_root} --force] cmd += if source ["--path", (Pathname.new(source).expand_path / "builder").to_s] else - [ - "--git", "https://github.com/DataDog/libdatadog", - "--tag", "v#{Libdatadog::LIB_VERSION}", - "builder" - ] + flag, value = + if tag + ["--tag", tag] + elsif ref + ["--rev", ref] + else + ["--tag", "v#{Libdatadog::LIB_VERSION}"] + end + ["--git", LIBDATADOG_GIT_URL, flag, value, "--locked", "builder"] end cmd += ["--no-default-features", "--features", features] if features @@ -104,6 +128,11 @@ module BuildFromSource cmd end + # Normalize a value to nil when it is nil or blank, otherwise return it unchanged. + def presence(value) + value if value && !value.to_s.strip.empty? + end + # Environment variables required by the builder binary at runtime. # # The builder reads PROFILE, TARGET, and CARGO_PKG_VERSION via env::var() @@ -139,12 +168,25 @@ namespace :libdatadog do ruby_platform = BuildFromSource::Target.ruby_platform(host_triple) paths = BuildFromSource::Paths - # Install builder binary + # Install builder binary. source / tag / ref are mutually exclusive; precedence + # and validation are handled by cargo_install_cmd. source = ENV["LIBDATADOG_SOURCE"] + tag = ENV["LIBDATADOG_TAG"] + ref = ENV["LIBDATADOG_COMMIT"] features = ENV["LIBDATADOG_FEATURES"] - install_cmd = BuildFromSource::Builder.cargo_install_cmd(source: source, features: features) - info = source ? "local: #{source}" : "v#{Libdatadog::LIB_VERSION}" + install_cmd = BuildFromSource::Builder.cargo_install_cmd(source: source, tag: tag, ref: ref, features: features) + + info = + if source + "local: #{source}" + elsif tag + "tag #{tag}" + elsif ref + "commit #{ref}" + else + "v#{Libdatadog::LIB_VERSION}" + end puts "Installing builder (#{info})..." system(*install_cmd) || raise("Failed to install builder via cargo") From b2d7db89d32dc4d030a2087e773e1eabfc70a710 Mon Sep 17 00:00:00 2001 From: Julio Date: Wed, 10 Jun 2026 15:50:42 +0200 Subject: [PATCH 2/2] chore: consolidate all variants under one env var (LIBDATADOG_REF) --- README.md | 29 +++++----- tasks/build.rake | 134 +++++++++++++++++++++++++++-------------------- 2 files changed, 95 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index b0d464b..2e789b1 100644 --- a/README.md +++ b/README.md @@ -17,26 +17,31 @@ You can also run `bundle exec pry` for an interactive prompt that will allow you `bundle exec rake libdatadog:build` builds libdatadog from source for the current platform using libdatadog's own [`builder` crate](https://github.com/DataDog/libdatadog/tree/main/builder). It requires a Rust toolchain (plus `cmake` and autotools); the Nix dev shell provides all of these. -Artifacts are written to `vendor/libdatadog-//`. +The default build writes artifacts to `vendor/libdatadog-//` (the +location the gem is packaged from); explicit-ref builds (see below) write to a directory named +after the ref instead, so they are not mislabeled as the pinned release. -By default it builds the pinned `v` tag. The following environment variables -select what gets built. They are optional and **mutually exclusive** — set at most one -(setting more than one fails): +By default it builds the pinned `v` tag. Set the optional `LIBDATADOG_REF` +environment variable to build something else. Its value is always an explicit +`:` pair, where `kind` is one of: -- `LIBDATADOG_TAG` — build a specific git tag, e.g. `LIBDATADOG_TAG=v33.0.0`. -- `LIBDATADOG_COMMIT` — build a specific commit, e.g. `LIBDATADOG_COMMIT=`. -- `LIBDATADOG_SOURCE` — build from a local libdatadog checkout, e.g. `LIBDATADOG_SOURCE=/path/to/libdatadog`. - To build a branch, check it out locally and point this at the checkout. +- `tag` — a git tag, e.g. `LIBDATADOG_REF=tag:v33.0.0`. +- `branch` — a git branch, e.g. `LIBDATADOG_REF=branch:my-feature`. +- `commit` — a git commit, e.g. `LIBDATADOG_REF=commit:`. +- `path` — a local libdatadog checkout, e.g. `LIBDATADOG_REF=path:/path/to/libdatadog`. + +The kind is always stated explicitly (no guessing), so a value without a recognized prefix +fails fast. When `LIBDATADOG_REF` is unset, the pinned `v` tag is built. Independently of the above, `LIBDATADOG_FEATURES` sets a comma-separated cargo feature override and applies to any build. -Tag and commit builds pass `cargo install --locked`, so they reproducibly use the dependency -versions pinned in libdatadog's `Cargo.lock`. Local-source builds omit `--locked`, since the -checkout may be modified. +Git builds (tag/branch/commit) pass `cargo install --locked`, so they reproducibly use the +dependency versions pinned in libdatadog's `Cargo.lock`. Local-path builds omit `--locked`, +since the checkout may be modified. ```sh -LIBDATADOG_COMMIT= bundle exec rake libdatadog:build +LIBDATADOG_REF=commit: bundle exec rake libdatadog:build ``` ### Testing packaging locally diff --git a/tasks/build.rake b/tasks/build.rake index f0cb670..1e35c19 100644 --- a/tasks/build.rake +++ b/tasks/build.rake @@ -69,13 +69,15 @@ module BuildFromSource tmp / "cmake-out" end - # Vendor output tree - def vendor - root / "vendor" / "libdatadog-#{Libdatadog::LIB_VERSION}" + # Vendor output tree. `label` names the build (default: the pinned LIB_VERSION, + # which is the canonical, packageable location); explicit refs use a ref-derived + # label so artifacts are not mislabeled as the pinned release. + def vendor(label = Libdatadog::LIB_VERSION) + root / "vendor" / "libdatadog-#{label}" end - def vendor_target(ruby_platform = Target.ruby_platform) - vendor / ruby_platform + def vendor_target(ruby_platform = Target.ruby_platform, label: Libdatadog::LIB_VERSION) + vendor(label) / ruby_platform end end end @@ -83,44 +85,35 @@ module BuildFromSource module Builder LIBDATADOG_GIT_URL = "https://github.com/DataDog/libdatadog" + # Cargo flag used for each git ref kind. + GIT_FLAG = {tag: "--tag", branch: "--branch", commit: "--rev"}.freeze + + # Recognized ":" ref kinds: a local path plus the git ref kinds above. + VALID_KINDS = [:path, *GIT_FLAG.keys].freeze + class << self # Build the cargo install command for the builder crate's `release` binary. # - # The libdatadog code to build is selected from exactly one of the following, - # which are mutually exclusive (passing more than one raises): - # source: path to a local libdatadog checkout. Built via --path, WITHOUT - # --locked, since the checkout may be modified locally. - # tag: a git tag. Built via --git --tag --locked. - # ref: a git commit. Built via --git --rev --locked. - # When none are given, defaults to the pinned --tag v --locked. + # The libdatadog code to build is selected by `ref`, an explicit ":" + # string where kind is one of: + # path — a local libdatadog checkout. Built via --path, WITHOUT --locked, + # since the checkout may be modified locally. + # tag — a git tag. Built via --git --tag --locked. + # branch — a git branch. Built via --git --branch --locked. + # commit — a git commit. Built via --git --rev --locked. + # When ref is blank, defaults to the pinned --tag v --locked. # # Git builds pass --locked so they reproducibly use libdatadog's Cargo.lock. # features: optional comma-separated cargo feature override, appended in all cases. - def cargo_install_cmd(source: nil, tag: nil, ref: nil, features: nil) - source = presence(source) - tag = presence(tag) - ref = presence(ref) - features = presence(features) - - selected = {source: source, tag: tag, ref: ref}.select { |_, value| value } - if selected.size > 1 - raise "Only one of source, tag, ref may be set at a time (got: #{selected.keys.join(", ")})" - end + def cargo_install_cmd(ref: nil, features: nil) + kind, value = ref ? parse_ref(ref) : [:tag, "v#{Libdatadog::LIB_VERSION}"] cmd = %W[cargo install --bin release --root #{Paths.builder_root} --force] - cmd += if source - ["--path", (Pathname.new(source).expand_path / "builder").to_s] + cmd += if kind == :path + ["--path", (Pathname.new(value).expand_path / "builder").to_s] else - flag, value = - if tag - ["--tag", tag] - elsif ref - ["--rev", ref] - else - ["--tag", "v#{Libdatadog::LIB_VERSION}"] - end - ["--git", LIBDATADOG_GIT_URL, flag, value, "--locked", "builder"] + ["--git", LIBDATADOG_GIT_URL, GIT_FLAG.fetch(kind), value, "--locked", "builder"] end cmd += ["--no-default-features", "--features", features] if features @@ -128,9 +121,27 @@ module BuildFromSource cmd end - # Normalize a value to nil when it is nil or blank, otherwise return it unchanged. - def presence(value) - value if value && !value.to_s.strip.empty? + # Parse an explicit ":" ref into [kind_symbol, value]. + # No auto-detection: a value with an unrecognized/missing kind prefix or an + # empty value is an error. + def parse_ref(ref) + kind, value = ref.split(":", 2) + kind = kind.to_sym if kind + + unless VALID_KINDS.include?(kind) && value && !value.empty? + raise %(LIBDATADOG_REF must be ":" where kind is one of ) + + %(#{VALID_KINDS.join(", ")} (e.g. tag:v33.0.0); got #{ref.inspect}) + end + + [kind, value] + end + + # Vendor directory label for a parsed ref ("libdatadog-