Skip to content

Commit c36d132

Browse files
zahclaude
andcommitted
feat: add lib.mkRubyRecorderPackage to flake and update Cargo.lock
Cherry-pick the nix native recorder derivation from the feat/nix-native-recorder-derivation branch. This adds the lib.mkRubyRecorderPackage function that the codetracer repo's nix/packages/default.nix expects. Also update Cargo.lock to match the bumped crate versions (codetracer_trace_types 0.16.3, codetracer_trace_writer 0.17.1). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dc89e99 commit c36d132

File tree

2 files changed

+238
-195
lines changed

2 files changed

+238
-195
lines changed

flake.nix

Lines changed: 110 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,99 @@
2323
file = ./rust-toolchain.toml;
2424
sha256 = "sha256-Qxt8XAuaUR2OMdKbN4u8dBJOhSHxS+uS06Wl9+flVEk=";
2525
};
26+
27+
# Helper function to build the native Ruby recorder for a given pkgs and Ruby.
28+
# Consumers can call this with their own nixpkgs and Ruby version to ensure
29+
# ABI compatibility (the native .so must match the Ruby that loads it).
30+
mkRubyRecorderPackage = pkgs: ruby: let
31+
inherit (pkgs) stdenv lib;
32+
isLinux = stdenv.isLinux;
33+
in stdenv.mkDerivation {
34+
pname = "ruby-recorder-native";
35+
version = builtins.readFile ./version.txt;
36+
37+
src = ./.;
38+
39+
nativeBuildInputs = [
40+
pkgs.rustc
41+
pkgs.cargo
42+
pkgs.rustPlatform.cargoSetupHook
43+
ruby # build.rs runs `ruby` to discover RbConfig paths
44+
pkgs.pkg-config
45+
pkgs.capnproto # codetracer_trace_format_capnp build.rs needs capnp
46+
pkgs.llvmPackages.libclang # bindgen (used by rb-sys) needs libclang
47+
] ++ lib.optionals stdenv.isDarwin [
48+
pkgs.libiconv
49+
pkgs.darwin.apple_sdk.frameworks.CoreFoundation
50+
pkgs.darwin.apple_sdk.frameworks.Security
51+
];
52+
53+
buildInputs = [ ruby ];
54+
55+
# bindgen needs LIBCLANG_PATH to find libclang.so
56+
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
57+
58+
# bindgen also needs C standard headers (stdio.h, stddef.h, etc.)
59+
BINDGEN_EXTRA_CLANG_ARGS = lib.optionalString isLinux (
60+
builtins.concatStringsSep " " [
61+
"-isystem ${stdenv.cc.libc.dev}/include"
62+
"-isystem ${pkgs.llvmPackages.libclang.lib}/lib/clang/${lib.versions.major pkgs.llvmPackages.libclang.version}/include"
63+
]
64+
);
65+
66+
cargoDeps = pkgs.rustPlatform.importCargoLock {
67+
lockFile = ./gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.lock;
68+
};
69+
70+
postUnpack = ''
71+
# cargoSetupHook expects Cargo.lock at the source root
72+
cp $sourceRoot/gems/codetracer-ruby-recorder/ext/native_tracer/Cargo.lock \
73+
$sourceRoot/Cargo.lock
74+
'';
75+
76+
preBuild = ''
77+
cd gems/codetracer-ruby-recorder/ext/native_tracer
78+
'';
79+
80+
buildPhase = ''
81+
runHook preBuild
82+
cargo build --release --offline
83+
runHook postBuild
84+
'';
85+
86+
installPhase = ''
87+
GEM_ROOT="$NIX_BUILD_TOP/$sourceRoot/gems/codetracer-ruby-recorder"
88+
89+
# Preserve gems/ path component — the native recorder's should_ignore_path()
90+
# in Rust uses "gems/" as an ignore pattern to avoid tracing kernel_patches.rb.
91+
mkdir -p $out/gems/bin $out/gems/lib/codetracer $out/gems/ext/native_tracer/target/release
92+
93+
# Copy compiled .so (Rust cdylib produces lib<name>.so on Linux, lib<name>.dylib on macOS)
94+
local dlext="${if isLinux then "so" else "dylib"}"
95+
cp target/release/libcodetracer_ruby_recorder.$dlext \
96+
$out/gems/ext/native_tracer/target/release/
97+
# Create the name the Ruby wrapper expects (codetracer_ruby_recorder.<dlext>)
98+
ln -s libcodetracer_ruby_recorder.$dlext \
99+
$out/gems/ext/native_tracer/target/release/codetracer_ruby_recorder.$dlext
100+
101+
# Copy Ruby wrapper files
102+
cp "$GEM_ROOT/lib/codetracer_ruby_recorder.rb" $out/gems/lib/
103+
cp "$GEM_ROOT/lib/codetracer/kernel_patches.rb" $out/gems/lib/codetracer/
104+
105+
# Copy bin entry script
106+
cp "$GEM_ROOT/bin/codetracer-ruby-recorder" $out/gems/bin/
107+
108+
# Top-level bin/ symlink so consumers' symlinkJoin picks it up
109+
mkdir -p $out/bin
110+
ln -s $out/gems/bin/codetracer-ruby-recorder $out/bin/codetracer-ruby-recorder
111+
'';
112+
113+
doCheck = false;
114+
};
26115
in {
116+
# Expose the helper function for consumers who need a custom Ruby version
117+
lib.mkRubyRecorderPackage = mkRubyRecorderPackage;
118+
27119
checks = forEachSystem (system: {
28120
pre-commit-check = pre-commit-hooks.lib.${system}.run {
29121
src = ./.;
@@ -104,15 +196,26 @@
104196

105197
packages = forEachSystem (system: let
106198
pkgs = import nixpkgs { inherit system; };
107-
buildGem = gemdir: pkgs.rubyPackages.buildRubyGem {
108-
pname = builtins.baseNameOf gemdir;
109-
version = builtins.readFile ./version.txt;
110-
src = gemdir;
111-
};
199+
ruby = pkgs.ruby;
112200
in {
113-
codetracer-ruby-recorder = buildGem ./gems/codetracer-ruby-recorder;
114-
codetracer-pure-ruby-recorder = buildGem ./gems/codetracer-pure-ruby-recorder;
201+
# Native Rust extension-based recorder (default)
202+
codetracer-ruby-recorder = mkRubyRecorderPackage pkgs ruby;
115203
default = self.packages.${system}.codetracer-ruby-recorder;
204+
205+
# Pure Ruby recorder (fallback, no compilation needed)
206+
codetracer-pure-ruby-recorder = pkgs.stdenv.mkDerivation {
207+
pname = "ruby-recorder-pure";
208+
version = builtins.readFile ./version.txt;
209+
src = ./.;
210+
dontInstall = true;
211+
buildPhase = ''
212+
mkdir -p $out/gems/bin $out/gems/lib
213+
cp -Lr ./gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder $out/gems/bin/
214+
cp -Lr ./gems/codetracer-pure-ruby-recorder/lib/* $out/gems/lib/
215+
mkdir -p $out/bin
216+
ln -s $out/gems/bin/codetracer-pure-ruby-recorder $out/bin/codetracer-pure-ruby-recorder
217+
'';
218+
};
116219
});
117220
};
118221
}

0 commit comments

Comments
 (0)