Skip to content

Allow or document workaround to include workspace-relative paths #359

@alexeagle

Description

@alexeagle

the legacy bazelruby/rules_ruby allowed code to include paths relative to the workspace root, such as

app/bin/rails

#!/usr/bin/env ruby
APP_PATH = Dir.pwd + "/app/config/application.rb"
require 'app/config/environment'
require 'rails/commands'

It seems this is not supported here because there's no includes attribute controllable by user of rb_library/rb_test?

Claude says it used to work as follows in bazelruby/rules_ruby:


  1. includes attribute → incpaths depset

ruby/private/tools/deps.bzl — the transitive_deps() function is the core:

  # deps.bzl ~line 45
  includes = [
      paths.join(workspace, inc)
      for inc in ctx.attr.includes
  ]

  return struct(
      incpaths = depset(
          direct = includes,          # this rule's includes, workspace-qualified
          transitive = deps.incpaths, # collected from all deps
          order = "topological",
      ),
      ...
  )

The helper _transitive_srcs pulls ruby_incpaths off the RubyLibraryInfo provider from each dep:

  def _transitive_srcs(deps):
      return struct(
          incpaths = [d[RubyLibraryInfo].ruby_incpaths for d in deps if RubyLibraryInfo in d],
          ...
      )

Yes, includes is fully transitive — each dep's incpaths depset is unioned in topological order.


  1. rb_library propagates via RubyLibraryInfo

ruby/private/library.bzl — the provider carries accumulated paths downstream:

  RubyLibraryInfo(
      transitive_ruby_srcs = deps.srcs,
      ruby_incpaths = deps.incpaths,  # entire transitive depset
      rubyopt = deps.rubyopt,
  )

  1. rb_binary injects paths into the wrapper template

ruby/private/binary.bzl ~line 65:

  ctx.actions.expand_template(
      template = ctx.file._wrapper_template,
      output = executable,
      substitutions = {
          "{loadpaths}": repr(deps.incpaths.to_list()),
          ...
      },
  )

  1. The wrapper script sets RUBYLIB (not -I flags directly)

ruby/private/binary_wrapper.tpl — the launcher uses RUBYLIB, not CLI -I:

  def create_loadpath_entries(custom, runfiles)
    [runfiles] + custom.map { |path| File.join(runfiles, path) }
  end

  def get_repository_imports(runfiles)
    Dir.entries(runfiles)
      .reject { |d| [".", ".."].include?(d) }
      .map    { |d| File.join(runfiles, d) }
      .select { |d| File.directory?(d) }
  end

  def main(args)
    custom_loadpaths = {loadpaths}   # ← substituted from incpaths.to_list()
    runfiles = find_runfiles

    loadpaths  = create_loadpath_entries(custom_loadpaths, runfiles)
    loadpaths += get_repository_imports(runfiles)   # ← IMPLICIT entries
    loadpaths += ENV['RUBYLIB'].split(':') if ENV.key?('RUBYLIB')
    ENV['RUBYLIB'] = loadpaths.sort.uniq.join(':')
  end

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions