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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions