Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Emacs backup files
*~

# Bazel Ignores
**/bazel-*
.bazelrc.user
Expand Down
3 changes: 3 additions & 0 deletions ruby/private/gem.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ def rb_gem(name, version, gem_name, srcs = [], **kwargs):
_gemspec_name = name + "_gemspec"
deps = kwargs.get("deps", [])
source_date_epoch = kwargs.pop("source_date_epoch", None)
strip_package = kwargs.pop("strip_package", "")
verbose = kwargs.pop("verbose", False)

_rb_gemspec(
name = _gemspec_name,
gem_name = gem_name,
version = version,
strip_package = strip_package,
**kwargs
)

Expand All @@ -28,5 +30,6 @@ def rb_gem(name, version, gem_name, srcs = [], **kwargs):
deps = srcs + deps,
visibility = ["//visibility:public"],
source_date_epoch = source_date_epoch,
strip_package = strip_package,
verbose = verbose,
)
5 changes: 5 additions & 0 deletions ruby/private/gem/dest_path.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def dest_path(f, pkg):
result = f.short_path
if pkg and result.startswith(pkg):
result = result[1+len(pkg):]
return result
16 changes: 14 additions & 2 deletions ruby/private/gem/gem.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("//ruby/private:providers.bzl", "RubyGem")
load("//ruby/private/gem:dest_path.bzl", _dest_path = "dest_path")

# Runs gem with arbitrary arguments
# eg: run_gem(runtime_ctx, ["install" "foo"])
Expand All @@ -8,15 +9,21 @@ def _rb_build_gem_impl(ctx):

_inputs = [ctx.file._gem_runner, metadata_file, gemspec]
_srcs = []

strip_package = ctx.attr.strip_package

for dep in ctx.attr.deps:
file_deps = dep.files.to_list()
_inputs.extend(file_deps)
for f in file_deps:
dest_path = _dest_path(f, strip_package)
_srcs.append({
"src_path": f.path,
"dest_path": f.short_path,
"dest_path": dest_path,
})

do_strip = (strip_package != "")

ctx.actions.write(
output = metadata_file,
content = struct(
Expand All @@ -25,6 +32,7 @@ def _rb_build_gem_impl(ctx):
output_path = ctx.outputs.gem.path,
source_date_epoch = ctx.attr.source_date_epoch,
verbose = ctx.attr.verbose,
do_strip = do_strip,
).to_json(),
)

Expand Down Expand Up @@ -56,7 +64,7 @@ _ATTRS = {
cfg = "host",
),
"_gem_runner": attr.label(
default = Label("@coinbase_rules_ruby//ruby/private/gem:gem_runner.rb"),
default = ":gem_runner.rb",
allow_single_file = True,
),
"gemspec": attr.label(
Expand All @@ -71,6 +79,10 @@ _ATTRS = {
"source_date_epoch": attr.string(
doc = "Sets source_date_epoch env var which should make output gems hermetic",
),
"strip_package": attr.string(
default = "",
doc = "strip this dir prefix from file paths added to the gem, such as package_name()",
),
"verbose": attr.bool(default = False),
}

Expand Down
17 changes: 15 additions & 2 deletions ruby/private/gem/gem_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,24 @@ def parse_opts
metadata_file
end

def copy_srcs(dir, srcs, verbose)
def copy_srcs(dir, srcs, verbose, do_strip)
# Sources need to be moved from their bazel_out locations
# to the correct folder in the ruby gem.
srcs.each do |src|
src_path = src['src_path']
dest_path = src['dest_path']

if do_strip and File.directory?(src_path)
# Lop off the leading path element.
# If that was the only path element,
# copy the source dir's contents to the dest,
# rather than the source itself.
dest_path = dest_path.split('/', 2)[1..].join('/')
if dest_path == ''
src_path += '/.'
end
end

tmpname = File.join(dir, File.dirname(dest_path))
FileUtils.mkdir_p(tmpname)
puts "copying #{src_path} to #{tmpname}" if verbose
Expand Down Expand Up @@ -80,8 +92,9 @@ def build_gem(metadata)
# We copy all related files to a tmpdir, build the entire gem in that tmpdir
# and then copy the output gem into the correct bazel output location.
verbose = metadata['verbose']
do_strip = metadata['do_strip']
Dir.mktmpdir do |dir|
copy_srcs(dir, metadata['srcs'], verbose)
copy_srcs(dir, metadata['srcs'], verbose, do_strip)
copy_gemspec(dir, metadata['gemspec_path'])
do_build(dir, metadata['gemspec_path'], metadata['output_path'])
end
Expand Down
22 changes: 16 additions & 6 deletions ruby/private/gem/gemspec.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,31 @@ load(
"RubyGem",
"RubyLibrary",
)
load("//ruby/private/gem:dest_path.bzl", _dest_path = "dest_path")

def _get_transitive_srcs(srcs, deps):
return depset(
srcs,
transitive = [dep[RubyLibrary].transitive_ruby_srcs for dep in deps],
)

def _rb_gem_impl(ctx):
def _rb_gemspec_impl(ctx):
gemspec = ctx.actions.declare_file("{}.gemspec".format(ctx.attr.gem_name))
metadata_file = ctx.actions.declare_file("{}_metadata".format(ctx.attr.gem_name))

_ruby_files = []
file_deps = _get_transitive_srcs([], ctx.attr.deps).to_list()

strip_package = ctx.attr.strip_package

for f in file_deps:
# For some files the src_path and dest_path will be the same, but
# for othrs the src_path will be in bazel)out while the dest_path
# for others the src_path will be in bazel-out while the dest_path
# will be from the workspace root.
dest_path = _dest_path(f, strip_package)
_ruby_files.append({
"src_path": f.path,
"dest_path": f.short_path,
"dest_path": dest_path,
})

ctx.actions.write(
Expand All @@ -39,6 +44,7 @@ def _rb_gem_impl(ctx):
licenses = ctx.attr.licenses,
require_paths = ctx.attr.require_paths,
gem_runtime_dependencies = ctx.attr.gem_runtime_dependencies,
do_strip = (strip_package != ""),
).to_json(),
)

Expand Down Expand Up @@ -101,7 +107,7 @@ _ATTRS = {
"require_paths": attr.string_list(),
"_gemspec_template": attr.label(
allow_single_file = True,
default = "gemspec_template.tpl",
default = ":gemspec_template.tpl",
),
"ruby_sdk": attr.string(
default = "@org_ruby_lang_ruby_toolchain",
Expand All @@ -113,13 +119,17 @@ _ATTRS = {
cfg = "host",
),
"_gemspec_builder": attr.label(
default = Label("@coinbase_rules_ruby//ruby/private/gem:gemspec_builder.rb"),
default = ":gemspec_builder.rb",
allow_single_file = True,
),
"strip_package": attr.string(
default = "",
doc = "strip this dir prefix from file paths added to the gem, such as package_name()",
),
}

rb_gemspec = rule(
implementation = _rb_gem_impl,
implementation = _rb_gemspec_impl,
attrs = _ATTRS,
provides = [DefaultInfo, RubyGem],
)
14 changes: 13 additions & 1 deletion ruby/private/gem/gemspec_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,32 @@ def expand_src_dirs(metadata)
# Files and required paths can include a directory which gemspec
# cannot handle. This will convert directories to individual files
srcs = metadata['raw_srcs']
do_strip = metadata['do_strip']

new_srcs = []
dests = []
srcs.each do |src|
src_path = src['src_path']
dest_path = src['dest_path']
if File.directory?(src_path)
Dir.glob("#{src_path}/**/*") do |f|
# expand the directory, replacing each src path with its dest path
new_srcs << f.gsub(src_path, dest_path) if File.file?(f)
if File.file?(f)
g = f.gsub(src_path, dest_path)
new_srcs << g
if do_strip
dests << g.sub(/^[^\/]+\//, '')
else
dests << g
end
end
end
elsif File.file?(src_path)
new_srcs << dest_path
end
end
metadata['srcs'] = new_srcs
metadata['dests'] = dests
metadata
end

Expand Down
2 changes: 1 addition & 1 deletion ruby/private/gem/gemspec_template.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Gem::Specification.new do |s|
s.authors = {authors}
s.version = "{version}"
s.licenses = {licenses}
s.files = {srcs}
s.files = {dests}
s.require_paths = {require_paths}

{gem_runtime_dependencies}
Expand Down