Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/proto/app/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rb_test(
srcs = ["hello_test.rb"],
main = "hello_test.rb",
deps = [
"//proto:foo_proto",
"//proto:all_protos",
"@bundle",
],
)
7 changes: 0 additions & 7 deletions examples/proto/app/hello.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
# TODO: is there a ruby runfiles helper?
runfiles_dir = ENV["RUNFILES_DIR"]
if runfiles_dir
main_root = File.join(runfiles_dir, "_main")
$LOAD_PATH.unshift(main_root) if Dir.exist?(main_root) && !$LOAD_PATH.include?(main_root)
end

require "grpc"
require "proto/foo_pb"
require "proto/foo_services_pb"
Expand Down
8 changes: 0 additions & 8 deletions examples/proto/app/hello_test.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
#!/usr/bin/env ruby
# Test that the gRPC app starts and can process a GetFoo RPC.

runfiles_dir = ENV["RUNFILES_DIR"]
if runfiles_dir
main_root = File.join(runfiles_dir, "_main")
$LOAD_PATH.unshift(main_root) if Dir.exist?(main_root) && !$LOAD_PATH.include?(main_root)
proto_root = File.join(main_root, "proto")
$LOAD_PATH.unshift(proto_root) if Dir.exist?(proto_root) && !$LOAD_PATH.include?(proto_root)
end

require "grpc"
require "proto/foo_pb"
require "proto/foo_services_pb"
Expand Down
11 changes: 11 additions & 0 deletions examples/proto/proto/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,14 @@ proto_library(
visibility = ["//visibility:public"],
deps = ["@protobuf//:timestamp_proto"],
)

# Bundles the other protos without compiling any sources of its own,
# like the `all_protos` style targets in envoy_api.
proto_library(
name = "all_protos",
visibility = ["//visibility:public"],
deps = [
":foo_proto",
":subdir_proto",
],
)
7 changes: 6 additions & 1 deletion ruby/private/binary.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ load(
"get_bundle_env",
"get_transitive_data",
"get_transitive_deps",
"get_transitive_load_paths",
"get_transitive_runfiles",
"get_transitive_srcs",
)
Expand Down Expand Up @@ -75,7 +76,7 @@ Supports `$(location)` expansion for targets from `srcs`, `data` and `deps`.
}

# buildifier: disable=function-docstring
def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {}, java_bin = "", jars_home_strip_suffix = "", coverage_helper = "", runfiles_helper = "", is_jruby = False):
def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {}, java_bin = "", jars_home_strip_suffix = "", coverage_helper = "", runfiles_helper = "", is_jruby = False, load_paths = []):
toolchain = ctx.toolchains["@rules_ruby//ruby:toolchain_type"]
if ctx.attr.ruby != None:
toolchain = ctx.attr.ruby[platform_common.ToolchainInfo]
Expand Down Expand Up @@ -140,6 +141,7 @@ def generate_rb_binary_script(ctx, binary, bundler = False, args = [], env = {},
"{locate_binary_in_runfiles}": locate_binary_in_runfiles,
"{coverage_helper}": coverage_helper,
"{runfiles_helper}": runfiles_helper,
"{load_paths}": " ".join(['"%s"' % path for path in load_paths]),
"{is_jruby}": "true" if is_jruby else "",
},
)
Expand All @@ -157,6 +159,7 @@ def rb_binary_impl(ctx):
# TODO: avoid expanding the depset to a list, it may be expensive in a large graph
transitive_data = get_transitive_data(ctx.files.data, ctx.attr.deps)
transitive_deps = get_transitive_deps(ctx.attr.deps)
transitive_load_paths = get_transitive_load_paths([], ctx.attr.deps)
transitive_srcs = get_transitive_srcs(ctx.files.srcs, ctx.attr.deps)

ruby_toolchain = ctx.toolchains["@rules_ruby//ruby:toolchain_type"]
Expand Down Expand Up @@ -229,6 +232,7 @@ def rb_binary_impl(ctx):
coverage_helper = _to_rlocation_path(ctx, ctx.file._coverage_helper),
runfiles_helper = _to_rlocation_path(ctx, ctx.file._runfiles_helper),
is_jruby = ruby_toolchain.version.startswith("jruby"),
load_paths = transitive_load_paths.to_list(),
)

return [
Expand All @@ -241,6 +245,7 @@ def rb_binary_impl(ctx):
binary = executable,
transitive_data = depset(tools, transitive = [transitive_data]),
transitive_deps = transitive_deps,
transitive_load_paths = transitive_load_paths,
transitive_srcs = transitive_srcs,
),
RunEnvironmentInfo(
Expand Down
6 changes: 6 additions & 0 deletions ruby/private/binary/binary.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export RUNFILES_DIR="$(realpath "${RUNFILES_DIR:-$0.runfiles}")"
# Set RUBYLIB to include the runfiles library.
export RUBYLIB="$(dirname "$(dirname "$(rlocation "{runfiles_helper}")")")${RUBYLIB:+:${RUBYLIB}}"

# Set RUBYLIB to include load paths of dependencies (e.g. generated protobuf code).
# These are directories, which rlocation can't resolve, so join them to RUNFILES_DIR.
for load_path in {load_paths}; do
RUBYLIB="${RUNFILES_DIR}/${load_path}:${RUBYLIB}"
done

# Find location of Ruby in runfiles.
export PATH=$(dirname $(rlocation {ruby})):$PATH

Expand Down
2 changes: 2 additions & 0 deletions ruby/private/library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ load(
"RubyFilesInfo",
"get_transitive_data",
"get_transitive_deps",
"get_transitive_load_paths",
"get_transitive_runfiles",
"get_transitive_srcs",
)
Expand Down Expand Up @@ -45,6 +46,7 @@ def _rb_library_impl(ctx):
binary = None,
transitive_data = depset(transitive_data),
transitive_deps = depset(transitive_deps),
transitive_load_paths = get_transitive_load_paths([], ctx.attr.deps),
transitive_srcs = depset(transitive_srcs),
),
]
Expand Down
46 changes: 37 additions & 9 deletions ruby/private/proto.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,51 @@ This file is loaded by `rules_ruby` (see its `rb_library` implementation) using
"""

load("@protobuf//bazel/common:proto_info.bzl", "ProtoInfo")
load("//ruby/private:providers.bzl", "RubyFilesInfo")
load("//ruby/private:providers.bzl", "RubyFilesInfo", "get_transitive_load_paths")
load(":proto_common.bzl", "proto_common")

GRPC_PLUGIN_TOOLCHAIN = "@rules_ruby//ruby:ruby_grpc_protoc_plugin.toolchain_type"
PROTO_TOOLCHAIN = "@protobuf//bazel/private:proto_toolchain_type"
RUBY_OUTPUT_FILE = "{proto_file_basename}_pb.rb"
RUBY_SERVICE_OUTPUT_FILE = "{proto_file_basename}_services_pb.rb"

# The google-protobuf gem already ships Ruby code for these, and generating our
# own copies would shadow the gem's on the load path.
PROTOBUF_WELL_KNOWN_TYPES_REPO = Label("@protobuf//:BUILD").repo_name

def _ruby_files_info(ctx, srcs = [], load_paths = []):
deps = ctx.rule.attr.deps
return RubyFilesInfo(
transitive_srcs = depset(srcs, transitive = [
dep[RubyFilesInfo].transitive_srcs
for dep in deps
if RubyFilesInfo in dep
]),
transitive_load_paths = get_transitive_load_paths(load_paths, deps),
transitive_deps = depset(),
transitive_data = depset(),
)

def _runfiles_load_path(ctx, output_root, proto_outdir):
"""Converts a protoc output directory into a path relative to the runfiles root."""
path = proto_outdir.removeprefix(output_root.path).removeprefix("/")
if path.startswith("external/"):
return path.removeprefix("external/")
if path.startswith("../"):
return path.removeprefix("../")
return ctx.workspace_name + "/" + path if path else ctx.workspace_name

def _ruby_proto_aspect_impl(target, ctx):
if not ProtoInfo in target:
return []

proto_info = target[ProtoInfo]

# Aggregator proto_library targets (deps only, no srcs) have nothing to compile.
is_aggregator = not proto_info.direct_sources
if is_aggregator or target.label.repo_name == PROTOBUF_WELL_KNOWN_TYPES_REPO:
return [_ruby_files_info(ctx)]

protoc_info = ctx.exec_groups["proto"].toolchains[PROTO_TOOLCHAIN].proto
grpc_info = ctx.exec_groups["proto"].toolchains[GRPC_PLUGIN_TOOLCHAIN].proto
msg_outputs = proto_common.declare_generated_files(ctx.actions, proto_info, "_pb.rb")
Expand Down Expand Up @@ -64,14 +96,10 @@ def _ruby_proto_aspect_impl(target, ctx):
)

return [
RubyFilesInfo(
transitive_srcs = depset(msg_outputs + service_outputs, transitive = [
dep[RubyFilesInfo].transitive_srcs
for dep in ctx.rule.attr.deps
if RubyFilesInfo in dep
]),
transitive_deps = depset(),
transitive_data = depset(),
_ruby_files_info(
ctx,
srcs = msg_outputs + service_outputs,
load_paths = [_runfiles_load_path(ctx, msg_outputs[0].root, proto_outdir)],
),
]

Expand Down
20 changes: 20 additions & 0 deletions ruby/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ RubyFilesInfo = provider(
"binary": "Main Ruby script.",
"transitive_data": "Transitive data files to add to runfiles.",
"transitive_deps": "Transitive dependencies to get files from.",
"transitive_load_paths": "Transitive runfiles-relative directories to add to the Ruby load path.",
"transitive_srcs": "Transitive Ruby files.",
},
)
Expand Down Expand Up @@ -59,6 +60,25 @@ def get_transitive_data(data, deps):
transitive = [dep[RubyFilesInfo].transitive_data for dep in deps],
)

def get_transitive_load_paths(load_paths, deps):
"""Obtain the load path directories for a target and its transitive dependencies.

Args:
load_paths: a list of runfiles-relative directories
deps: a list of targets that are direct dependencies
Returns:
a collection of the transitive load path directories
"""
return depset(
load_paths,
transitive = [
# Not every RubyFilesInfo producer sets this field.
getattr(dep[RubyFilesInfo], "transitive_load_paths", None) or depset()
for dep in deps
if RubyFilesInfo in dep
],
)

def get_transitive_deps(deps):
"""Obtain the dependencies for a target and its transitive dependencies.

Expand Down