From 3de6c8182ae077067edffc4aa32d933338de3c3d Mon Sep 17 00:00:00 2001 From: dhash Date: Wed, 5 Aug 2026 14:03:01 -0700 Subject: [PATCH 1/4] feat: add extra_args passthrough to bundle install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Threads extra command-line arguments to `bundle install` from the `ruby.bundle_fetch` bzlmod tag / `rb_bundle_fetch` repo rule / `rb_bundle_install` rule down onto the install command line. Primary use case: cross-platform bundles. With bundler's `--target-rbconfig` (RubyGems 3.4+ / Gem::TargetRbConfig), the host ruby can install a DIFFERENT platform's precompiled gems — e.g. assembling an x86_64-linux vendor/bundle on an arm64-darwin host for a container image layer, without a linux executor: ruby.bundle_fetch( name = "bundle", gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", extra_args = ["--target-rbconfig", "/path/to/linux/rbconfig.rb"], ) Covers both the bzlmod and WORKSPACE paths (shared rb_bundle_fetch repo rule) and the standalone rb_bundle_install rule. --- ruby/extensions.bzl | 2 ++ ruby/private/bundle_fetch.bzl | 6 ++++++ ruby/private/bundle_fetch/BUILD.tpl | 1 + ruby/private/bundle_install.bzl | 6 ++++++ ruby/private/bundle_install/bundle_install.cmd.tpl | 2 +- ruby/private/bundle_install/bundle_install.sh.tpl | 2 +- 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ruby/extensions.bzl b/ruby/extensions.bzl index cf7bf4f64..0e58d8253 100644 --- a/ruby/extensions.bzl +++ b/ruby/extensions.bzl @@ -38,6 +38,7 @@ ruby_bundle_fetch = tag_class(attrs = { "name": attr.string(doc = "Resulting repository name for the bundle"), "srcs": attr.label_list(), "env": attr.string_dict(), + "extra_args": attr.string_list(doc = "Extra arguments appended to `bundle install`."), "gemfile": attr.label(), "gemfile_lock": attr.label(), "gem_checksums": attr.string_dict(), @@ -99,6 +100,7 @@ def _ruby_module_extension(module_ctx): name = bundle_fetch.name, srcs = bundle_fetch.srcs, env = bundle_fetch.env, + extra_args = bundle_fetch.extra_args, gemfile = bundle_fetch.gemfile, gemfile_lock = bundle_fetch.gemfile_lock, gem_checksums = bundle_fetch.gem_checksums, diff --git a/ruby/private/bundle_fetch.bzl b/ruby/private/bundle_fetch.bzl index 942c80e8a..1f0a68183 100644 --- a/ruby/private/bundle_fetch.bzl +++ b/ruby/private/bundle_fetch.bzl @@ -247,6 +247,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "{gem_fragments}": "".join(gem_fragments), "{gem_install_fragments}": "".join(gem_install_fragments), "{env}": repr(repository_ctx.attr.env), + "{extra_args}": repr(repository_ctx.attr.extra_args), "{ruby}": ruby_toolchain_attr, }, ) @@ -258,6 +259,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "gemfile_lock": repository_ctx.attr.gemfile_lock, "srcs": repository_ctx.attr.srcs, "env": repository_ctx.attr.env, + "extra_args": repository_ctx.attr.extra_args, "bundler_remote": repository_ctx.attr.bundler_remote, "bundler_checksums": repository_ctx.attr.bundler_checksums, "gem_checksums": gem_checksums, @@ -301,6 +303,10 @@ rb_bundle_fetch = repository_rule( "env": attr.string_dict( doc = "Environment variables to use during installation.", ), + "extra_args": attr.string_list( + doc = "Extra arguments appended to the `bundle install` command line " + + "run by the generated rb_bundle_install target.", + ), "bundler_remote": attr.string( default = "https://rubygems.org/", doc = "Remote to fetch the bundler gem from.", diff --git a/ruby/private/bundle_fetch/BUILD.tpl b/ruby/private/bundle_fetch/BUILD.tpl index a0f192b0d..7d52ca5b8 100644 --- a/ruby/private/bundle_fetch/BUILD.tpl +++ b/ruby/private/bundle_fetch/BUILD.tpl @@ -8,6 +8,7 @@ rb_bundle_install( name = "{name}", srcs = {srcs}, env = {env}, + extra_args = {extra_args}, gemfile = "{gemfile_path}", gemfile_lock = "{gemfile_lock_path}", jars = glob(["{jars_path}/**/*.jar"], allow_empty = True), diff --git a/ruby/private/bundle_install.bzl b/ruby/private/bundle_install.bzl index 898410310..33e2ca586 100644 --- a/ruby/private/bundle_install.bzl +++ b/ruby/private/bundle_install.bzl @@ -112,6 +112,7 @@ def _rb_bundle_install_impl(ctx): "{env}": _convert_env_to_script(ctx, env), "{bundler_exe}": _normalize_path(ctx, bundler_exe), "{ruby_path}": _normalize_path(ctx, toolchain.ruby.path), + "{extra_args}": " ".join(ctx.attr.extra_args), }, ) @@ -185,6 +186,11 @@ rb_bundle_install = rule( "env": attr.string_dict( doc = "Environment variables to use during installation.", ), + "extra_args": attr.string_list( + doc = "Extra arguments appended to the `bundle install` command line. " + + "For example `[\"--target-rbconfig\", \"$(location //path:rbconfig.rb)\"]` " + + "to install a different platform's precompiled gems (cross-platform bundle).", + ), "ruby": attr.label( doc = "Override Ruby toolchain to use when installing the gem.", providers = [platform_common.ToolchainInfo], diff --git a/ruby/private/bundle_install/bundle_install.cmd.tpl b/ruby/private/bundle_install/bundle_install.cmd.tpl index f42c753fb..96cc07eb8 100644 --- a/ruby/private/bundle_install/bundle_install.cmd.tpl +++ b/ruby/private/bundle_install/bundle_install.cmd.tpl @@ -2,7 +2,7 @@ {env} -{ruby_path} {bundler_exe} install --standalone --local +{ruby_path} {bundler_exe} install --standalone --local {extra_args} {ruby_path} {bundler_exe} binstubs --all :: vim: ft=dosbatch diff --git a/ruby/private/bundle_install/bundle_install.sh.tpl b/ruby/private/bundle_install/bundle_install.sh.tpl index 738a03786..4b9b705a9 100644 --- a/ruby/private/bundle_install/bundle_install.sh.tpl +++ b/ruby/private/bundle_install/bundle_install.sh.tpl @@ -2,7 +2,7 @@ {env} -{ruby_path} {bundler_exe} install --standalone --local +{ruby_path} {bundler_exe} install --standalone --local {extra_args} {ruby_path} {bundler_exe} binstubs --all # vim: ft=bash From f4cce4e22dc5b76dbc2426585acfe8dee66f70f3 Mon Sep 17 00:00:00 2001 From: dhash Date: Wed, 5 Aug 2026 15:00:24 -0700 Subject: [PATCH 2/4] feat: expand $(location) in extra_args via new data attr extra_args now supports $(location)/$(rootpath)/$(execpath) make-variable expansion (built-in ctx.expand_location) against a new `data` label_list, which is also threaded as inputs to the bundle install action. This lets a file-valued flag reference a target instead of a raw path: ruby.bundle_fetch( name = "bundle_linux_amd64", gemfile = "//:Gemfile", gemfile_lock = "//:Gemfile.lock", data = ["//image:x86_64-linux-rbconfig.rb"], extra_args = ["--target-rbconfig", "$(location //image:x86_64-linux-rbconfig.rb)"], ) Threaded through the bzlmod tag, repo rule, generated BUILD, and install rule. --- ruby/extensions.bzl | 4 +++- ruby/private/bundle_fetch.bzl | 9 ++++++++- ruby/private/bundle_fetch/BUILD.tpl | 1 + ruby/private/bundle_install.bzl | 16 +++++++++++++--- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/ruby/extensions.bzl b/ruby/extensions.bzl index 0e58d8253..bac459a4c 100644 --- a/ruby/extensions.bzl +++ b/ruby/extensions.bzl @@ -38,7 +38,8 @@ ruby_bundle_fetch = tag_class(attrs = { "name": attr.string(doc = "Resulting repository name for the bundle"), "srcs": attr.label_list(), "env": attr.string_dict(), - "extra_args": attr.string_list(doc = "Extra arguments appended to `bundle install`."), + "extra_args": attr.string_list(doc = "Extra arguments appended to `bundle install`. Supports `$(location ...)` against `data`."), + "data": attr.label_list(doc = "Files referenced from `extra_args` via `$(location ...)`."), "gemfile": attr.label(), "gemfile_lock": attr.label(), "gem_checksums": attr.string_dict(), @@ -101,6 +102,7 @@ def _ruby_module_extension(module_ctx): srcs = bundle_fetch.srcs, env = bundle_fetch.env, extra_args = bundle_fetch.extra_args, + data = [str(label) for label in bundle_fetch.data], gemfile = bundle_fetch.gemfile, gemfile_lock = bundle_fetch.gemfile_lock, gem_checksums = bundle_fetch.gem_checksums, diff --git a/ruby/private/bundle_fetch.bzl b/ruby/private/bundle_fetch.bzl index 1f0a68183..0d1d99e6e 100644 --- a/ruby/private/bundle_fetch.bzl +++ b/ruby/private/bundle_fetch.bzl @@ -248,6 +248,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "{gem_install_fragments}": "".join(gem_install_fragments), "{env}": repr(repository_ctx.attr.env), "{extra_args}": repr(repository_ctx.attr.extra_args), + "{data}": _join_and_indent(repository_ctx.attr.data), "{ruby}": ruby_toolchain_attr, }, ) @@ -260,6 +261,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "srcs": repository_ctx.attr.srcs, "env": repository_ctx.attr.env, "extra_args": repository_ctx.attr.extra_args, + "data": repository_ctx.attr.data, "bundler_remote": repository_ctx.attr.bundler_remote, "bundler_checksums": repository_ctx.attr.bundler_checksums, "gem_checksums": gem_checksums, @@ -305,7 +307,12 @@ rb_bundle_fetch = repository_rule( ), "extra_args": attr.string_list( doc = "Extra arguments appended to the `bundle install` command line " + - "run by the generated rb_bundle_install target.", + "run by the generated rb_bundle_install target. Supports " + + "`$(location ...)` expansion against `data`.", + ), + "data": attr.string_list( + doc = "Labels (as canonical strings) referenced from `extra_args` via " + + "`$(location ...)`; forwarded to the generated rb_bundle_install `data`.", ), "bundler_remote": attr.string( default = "https://rubygems.org/", diff --git a/ruby/private/bundle_fetch/BUILD.tpl b/ruby/private/bundle_fetch/BUILD.tpl index 7d52ca5b8..f2ff595a7 100644 --- a/ruby/private/bundle_fetch/BUILD.tpl +++ b/ruby/private/bundle_fetch/BUILD.tpl @@ -7,6 +7,7 @@ package(default_visibility = ["//visibility:public"]) rb_bundle_install( name = "{name}", srcs = {srcs}, + data = {data}, env = {env}, extra_args = {extra_args}, gemfile = "{gemfile_path}", diff --git a/ruby/private/bundle_install.bzl b/ruby/private/bundle_install.bzl index 33e2ca586..8f514cd18 100644 --- a/ruby/private/bundle_install.bzl +++ b/ruby/private/bundle_install.bzl @@ -112,13 +112,16 @@ def _rb_bundle_install_impl(ctx): "{env}": _convert_env_to_script(ctx, env), "{bundler_exe}": _normalize_path(ctx, bundler_exe), "{ruby_path}": _normalize_path(ctx, toolchain.ruby.path), - "{extra_args}": " ".join(ctx.attr.extra_args), + "{extra_args}": " ".join([ + ctx.expand_location(arg, ctx.attr.data) + for arg in ctx.attr.extra_args + ]), }, ) ctx.actions.run( executable = script, - inputs = depset([ctx.file.gemfile, ctx.file.gemfile_lock] + ctx.files.srcs + ctx.files.gems + jar_files), + inputs = depset([ctx.file.gemfile, ctx.file.gemfile_lock] + ctx.files.srcs + ctx.files.data + ctx.files.gems + jar_files), outputs = [binstubs, bundle_path], mnemonic = "BundleInstall", progress_message = "Running bundle install (%{label})", @@ -188,9 +191,16 @@ rb_bundle_install = rule( ), "extra_args": attr.string_list( doc = "Extra arguments appended to the `bundle install` command line. " + - "For example `[\"--target-rbconfig\", \"$(location //path:rbconfig.rb)\"]` " + + "Supports `$(location ...)`/`$(rootpath ...)`/`$(execpath ...)` make-variable " + + "expansion against `data`. For example " + + "`[\"--target-rbconfig\", \"$(location //path:rbconfig.rb)\"]` " + "to install a different platform's precompiled gems (cross-platform bundle).", ), + "data": attr.label_list( + allow_files = True, + doc = "Files referenced from `extra_args` via `$(location ...)` expansion. " + + "They are also added as inputs to the `bundle install` action.", + ), "ruby": attr.label( doc = "Override Ruby toolchain to use when installing the gem.", providers = [platform_common.ToolchainInfo], From 0d5701a7f5809de6e85e8ca2117e650b7306ddd4 Mon Sep 17 00:00:00 2001 From: dhash Date: Wed, 5 Aug 2026 15:32:24 -0700 Subject: [PATCH 3/4] feat: expose dist_files filegroup for the ruby install tree Adds a public dist_files filegroup (glob dist/**/*) to each per-platform ruby repo, aliased on the @ruby hub. Lets you package the interpreter into a container image (portable-ruby is relocatable), e.g.: pkg_tar(name = "ruby_runtime", srcs = ["@ruby//:dist_files"], package_dir = "/usr/local", strip_prefix = "dist") --- ruby/private/download/BUILD.tpl | 11 +++++++++++ ruby/private/toolchain/hub.bzl | 1 + 2 files changed, 12 insertions(+) diff --git a/ruby/private/download/BUILD.tpl b/ruby/private/download/BUILD.tpl index c08bcdb2e..acdffbba3 100644 --- a/ruby/private/download/BUILD.tpl +++ b/ruby/private/download/BUILD.tpl @@ -13,6 +13,17 @@ filegroup( }), ) +# The complete Ruby install tree (bin/, lib/, include/, ...). Useful for +# packaging the interpreter into a container image (portable-ruby is relocatable +# via relative rpaths, so this tars cleanly to e.g. /usr/local). +filegroup( + name = "dist_files", + srcs = glob( + ["dist/**/*"], + allow_empty = True, + ), +) + rb_binary( name = "ruby", main = ":ruby_file", diff --git a/ruby/private/toolchain/hub.bzl b/ruby/private/toolchain/hub.bzl index d7c1c0613..1fe37a855 100644 --- a/ruby/private/toolchain/hub.bzl +++ b/ruby/private/toolchain/hub.bzl @@ -41,6 +41,7 @@ _STATIC_ALIASES = [ "toolchain", "headers", "jars", + "dist_files", ] _CONFIG_SETTING_TPL = """ From adce1b28e993f4923e492bb1f3c50c3ed22c31ad Mon Sep 17 00:00:00 2001 From: dhash Date: Wed, 5 Aug 2026 17:55:35 -0700 Subject: [PATCH 4/4] feat(bundle_install): cross-platform / container bundle support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the pieces needed to install a bundle for a FOREIGN platform (e.g. build a linux gems layer from a macOS host via --target-rbconfig) and package it into a container image: * env values now support $(location ...)/$(execpath ...) expansion against `data` (mirrors extra_args) — lets an env var reference a build artifact, e.g. prepend a generated cross-compiler wrapper dir onto PATH. * new `rbconfig` filegroup on the ruby dist (+ hub alias): the interpreter's own relocatable rbconfig.rb, for `gem install --target-rbconfig` cross-builds. * `binstubs` attr (default True): skip `bundle binstubs --all` for cross-platform bundles, where the host ruby can't validate the target's native extensions; the (empty) binstubs dir is still materialized. * `gems` output group: just the vendor/bundle tree (no Gemfile/binstubs), so consumers can lay the gems into a container BUNDLE_PATH with a clean strip_prefix. --- ruby/extensions.bzl | 2 + ruby/private/bundle_fetch.bzl | 11 ++++- ruby/private/bundle_fetch/BUILD.tpl | 1 + ruby/private/bundle_install.bzl | 48 +++++++++++++++++-- .../bundle_install/bundle_install.cmd.tpl | 2 +- .../bundle_install/bundle_install.sh.tpl | 2 +- ruby/private/download/BUILD.tpl | 13 +++++ ruby/private/toolchain/hub.bzl | 1 + 8 files changed, 72 insertions(+), 8 deletions(-) diff --git a/ruby/extensions.bzl b/ruby/extensions.bzl index bac459a4c..8507e76bb 100644 --- a/ruby/extensions.bzl +++ b/ruby/extensions.bzl @@ -40,6 +40,7 @@ ruby_bundle_fetch = tag_class(attrs = { "env": attr.string_dict(), "extra_args": attr.string_list(doc = "Extra arguments appended to `bundle install`. Supports `$(location ...)` against `data`."), "data": attr.label_list(doc = "Files referenced from `extra_args` via `$(location ...)`."), + "binstubs": attr.bool(default = True, doc = "Run `bundle binstubs --all` after install. Set False for cross-platform bundles."), "gemfile": attr.label(), "gemfile_lock": attr.label(), "gem_checksums": attr.string_dict(), @@ -103,6 +104,7 @@ def _ruby_module_extension(module_ctx): env = bundle_fetch.env, extra_args = bundle_fetch.extra_args, data = [str(label) for label in bundle_fetch.data], + binstubs = bundle_fetch.binstubs, gemfile = bundle_fetch.gemfile, gemfile_lock = bundle_fetch.gemfile_lock, gem_checksums = bundle_fetch.gem_checksums, diff --git a/ruby/private/bundle_fetch.bzl b/ruby/private/bundle_fetch.bzl index 0d1d99e6e..ccbf1192e 100644 --- a/ruby/private/bundle_fetch.bzl +++ b/ruby/private/bundle_fetch.bzl @@ -249,6 +249,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "{env}": repr(repository_ctx.attr.env), "{extra_args}": repr(repository_ctx.attr.extra_args), "{data}": _join_and_indent(repository_ctx.attr.data), + "{binstubs}": repr(repository_ctx.attr.binstubs), "{ruby}": ruby_toolchain_attr, }, ) @@ -262,6 +263,7 @@ def _rb_bundle_fetch_impl(repository_ctx): "env": repository_ctx.attr.env, "extra_args": repository_ctx.attr.extra_args, "data": repository_ctx.attr.data, + "binstubs": repository_ctx.attr.binstubs, "bundler_remote": repository_ctx.attr.bundler_remote, "bundler_checksums": repository_ctx.attr.bundler_checksums, "gem_checksums": gem_checksums, @@ -303,13 +305,20 @@ rb_bundle_fetch = repository_rule( doc = "List of Ruby source files necessary during installation.", ), "env": attr.string_dict( - doc = "Environment variables to use during installation.", + doc = "Environment variables to use during installation. Values support " + + "`$(location ...)` expansion against `data` (forwarded to the " + + "generated rb_bundle_install `env`).", ), "extra_args": attr.string_list( doc = "Extra arguments appended to the `bundle install` command line " + "run by the generated rb_bundle_install target. Supports " + "`$(location ...)` expansion against `data`.", ), + "binstubs": attr.bool( + default = True, + doc = "Forwarded to the generated rb_bundle_install `binstubs` (set False " + + "to skip `bundle binstubs --all` for cross-platform bundles).", + ), "data": attr.string_list( doc = "Labels (as canonical strings) referenced from `extra_args` via " + "`$(location ...)`; forwarded to the generated rb_bundle_install `data`.", diff --git a/ruby/private/bundle_fetch/BUILD.tpl b/ruby/private/bundle_fetch/BUILD.tpl index f2ff595a7..9537cf292 100644 --- a/ruby/private/bundle_fetch/BUILD.tpl +++ b/ruby/private/bundle_fetch/BUILD.tpl @@ -10,6 +10,7 @@ rb_bundle_install( data = {data}, env = {env}, extra_args = {extra_args}, + binstubs = {binstubs}, gemfile = "{gemfile_path}", gemfile_lock = "{gemfile_lock_path}", jars = glob(["{jars_path}/**/*.jar"], allow_empty = True), diff --git a/ruby/private/bundle_install.bzl b/ruby/private/bundle_install.bzl index 8f514cd18..03ff41d02 100644 --- a/ruby/private/bundle_install.bzl +++ b/ruby/private/bundle_install.bzl @@ -57,12 +57,20 @@ def _rb_bundle_install_impl(ctx): jar_files = ctx.files.jars if ctx.attr.jars else [] + # Expand `$(location ...)`/`$(execpath ...)` in env values (against `data`), + # mirroring `extra_args`. Lets an env var reference a build artifact by label + # — e.g. prepending a generated cross-compiler wrapper dir onto PATH. + attr_env = { + key: ctx.expand_location(value, ctx.attr.data) + for key, value in ctx.attr.env.items() + } + env = {} env.update(toolchain.env) - env.update(ctx.attr.env) + env.update(attr_env) bundler_env = {} - bundler_env.update(ctx.attr.env) + bundler_env.update(attr_env) jars_home_strip_suffix = "" if toolchain.version.startswith("jruby"): @@ -80,12 +88,12 @@ def _rb_bundle_install_impl(ctx): if _is_windows(ctx): script = ctx.actions.declare_file("bundle_install_{}.cmd".format(ctx.label.name)) template = ctx.file._bundle_install_cmd_tpl - path = ctx.attr.env.get("PATH", "%PATH%") + path = attr_env.get("PATH", "%PATH%") env.update({"PATH": _normalize_path(ctx, toolchain.ruby.dirname) + ";" + path}) else: script = ctx.actions.declare_file("bundle_install_{}.sh".format(ctx.label.name)) template = ctx.file._bundle_install_sh_tpl - path = ctx.attr.env.get("PATH", "$PATH") + path = attr_env.get("PATH", "$PATH") env.update({"PATH": toolchain.ruby.dirname + ":" + path}) # Calculate relative location between BUNDLE_GEMFILE and BUNDLE_PATH. @@ -105,6 +113,21 @@ def _rb_bundle_install_impl(ctx): "BUNDLE_SHEBANG": _normalize_path(ctx, toolchain.ruby.short_path), }) + # Binstubs generation runs with the HOST ruby, which validates gems against + # the running platform. For a cross-platform bundle (e.g. installed with + # --target-rbconfig for another OS/arch) the host can't see those gems' + # native extensions and `binstubs --all` fails. `binstubs = False` skips it, + # just materializing the (empty) declared binstubs dir instead. + if ctx.attr.binstubs: + binstubs_cmd = "{} {} binstubs --all".format( + _normalize_path(ctx, toolchain.ruby.path), + _normalize_path(ctx, bundler_exe), + ) + elif _is_windows(ctx): + binstubs_cmd = 'if not exist "{p}" mkdir "{p}"'.format(p = _normalize_path(ctx, binstubs.path)) + else: + binstubs_cmd = 'mkdir -p "{}"'.format(binstubs.path) + ctx.actions.expand_template( template = template, output = script, @@ -112,6 +135,7 @@ def _rb_bundle_install_impl(ctx): "{env}": _convert_env_to_script(ctx, env), "{bundler_exe}": _normalize_path(ctx, bundler_exe), "{ruby_path}": _normalize_path(ctx, toolchain.ruby.path), + "{binstubs_cmd}": binstubs_cmd, "{extra_args}": " ".join([ ctx.expand_location(arg, ctx.attr.data) for arg in ctx.attr.extra_args @@ -141,6 +165,11 @@ def _rb_bundle_install_impl(ctx): files = depset(files), runfiles = ctx.runfiles(files), ), + # `gems` exposes JUST the installed vendor/bundle tree (no Gemfile/ + # binstubs), so consumers can package it cleanly — e.g. + # `filegroup(output_group = "gems")` + pkg_files strip_prefix to lay the + # gems into a container's BUNDLE_PATH without the surrounding files. + OutputGroupInfo(gems = depset([bundle_path])), RubyFilesInfo( binary = None, transitive_srcs = depset([ctx.file.gemfile, ctx.file.gemfile_lock] + ctx.files.srcs), @@ -187,7 +216,16 @@ rb_bundle_install = rule( doc = "List of Ruby source files used to build the library.", ), "env": attr.string_dict( - doc = "Environment variables to use during installation.", + doc = "Environment variables to use during installation. Values support " + + "`$(location ...)`/`$(execpath ...)` make-variable expansion against " + + "`data` (e.g. prepend a generated cross-compiler wrapper dir onto PATH).", + ), + "binstubs": attr.bool( + default = True, + doc = "Whether to run `bundle binstubs --all` after install. Set False for " + + "cross-platform bundles (installed with a foreign --target-rbconfig): the " + + "host ruby can't validate the target's native extensions, so binstubs " + + "generation fails. When False the (empty) binstubs dir is still created.", ), "extra_args": attr.string_list( doc = "Extra arguments appended to the `bundle install` command line. " + diff --git a/ruby/private/bundle_install/bundle_install.cmd.tpl b/ruby/private/bundle_install/bundle_install.cmd.tpl index 96cc07eb8..789a05319 100644 --- a/ruby/private/bundle_install/bundle_install.cmd.tpl +++ b/ruby/private/bundle_install/bundle_install.cmd.tpl @@ -3,6 +3,6 @@ {env} {ruby_path} {bundler_exe} install --standalone --local {extra_args} -{ruby_path} {bundler_exe} binstubs --all +{binstubs_cmd} :: vim: ft=dosbatch diff --git a/ruby/private/bundle_install/bundle_install.sh.tpl b/ruby/private/bundle_install/bundle_install.sh.tpl index 4b9b705a9..78e060bde 100644 --- a/ruby/private/bundle_install/bundle_install.sh.tpl +++ b/ruby/private/bundle_install/bundle_install.sh.tpl @@ -3,6 +3,6 @@ {env} {ruby_path} {bundler_exe} install --standalone --local {extra_args} -{ruby_path} {bundler_exe} binstubs --all +{binstubs_cmd} # vim: ft=bash diff --git a/ruby/private/download/BUILD.tpl b/ruby/private/download/BUILD.tpl index acdffbba3..082539750 100644 --- a/ruby/private/download/BUILD.tpl +++ b/ruby/private/download/BUILD.tpl @@ -24,6 +24,19 @@ filegroup( ), ) +# The interpreter's own rbconfig.rb (a single file). Because portable-ruby is +# relocatable (rbconfig computes TOPDIR from __FILE__), passing this unmodified +# to `gem install --target-rbconfig` cross-compiles source gems for THIS Ruby's +# platform — its rubyhdrdir/libdir auto-resolve to the staged `dist` tree. Under +# a platform transition it resolves to the target arch's Ruby. +filegroup( + name = "rbconfig", + srcs = glob( + ["dist/lib/ruby/*/*/rbconfig.rb"], + allow_empty = True, + ), +) + rb_binary( name = "ruby", main = ":ruby_file", diff --git a/ruby/private/toolchain/hub.bzl b/ruby/private/toolchain/hub.bzl index 1fe37a855..3973c31db 100644 --- a/ruby/private/toolchain/hub.bzl +++ b/ruby/private/toolchain/hub.bzl @@ -42,6 +42,7 @@ _STATIC_ALIASES = [ "headers", "jars", "dist_files", + "rbconfig", ] _CONFIG_SETTING_TPL = """