Skip to content

Commit 05bb2b9

Browse files
authored
Gemspec generation needs to handle directories (#20)
* Change gemspec build to handle directories
1 parent 5c99414 commit 05bb2b9

File tree

4 files changed

+129
-32
lines changed

4 files changed

+129
-32
lines changed

ruby/private/gem/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exports_files(
44
[
55
"gemspec_template.tpl",
66
"gem_runner.rb",
7+
"gemspec_builder.rb",
78
],
89
visibility = ["//visibility:public"],
910
)

ruby/private/gem/gemspec.bzl

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,42 @@ def _get_transitive_srcs(srcs, deps):
1414
transitive = [dep[RubyLibrary].transitive_ruby_srcs for dep in deps],
1515
)
1616

17-
def _unique_elems(list):
18-
_out = []
19-
_prev = None
20-
for elem in sorted(list):
21-
if _prev != elem:
22-
_out.append(elem)
23-
24-
return _out
25-
2617
def _rb_gem_impl(ctx):
27-
gemspec = ctx.actions.declare_file("%s.gemspec" % ctx.attr.gem_name)
18+
gemspec = ctx.actions.declare_file("{}.gemspec".format(ctx.attr.gem_name))
19+
metadata_file = ctx.actions.declare_file("{}_metadata".format(ctx.attr.gem_name))
2820

2921
_ruby_files = []
30-
_require_paths = []
31-
for file in _get_transitive_srcs([], ctx.attr.deps).to_list():
32-
_ruby_files.append(file.short_path)
33-
_require_paths.append(file.dirname)
22+
file_deps = _get_transitive_srcs([], ctx.attr.deps).to_list()
23+
for f in file_deps:
24+
_ruby_files.append(f.short_path)
3425

35-
_require_paths = _unique_elems(_require_paths) # Set is not supported in Starlark
26+
ctx.actions.write(
27+
output = metadata_file,
28+
content = struct(
29+
name = ctx.attr.gem_name,
30+
srcs = _ruby_files,
31+
authors = ctx.attr.authors,
32+
version = ctx.attr.version,
33+
).to_json(),
34+
)
3635

37-
ctx.actions.expand_template(
38-
template = ctx.file._gemspec_template,
39-
output = gemspec,
40-
substitutions = {
41-
"{name}": "\"%s\"" % ctx.attr.gem_name,
42-
"{srcs}": repr(_ruby_files),
43-
"{authors}": repr(ctx.attr.authors),
44-
"{version}": "\"{}\"".format(ctx.attr.version),
45-
"{require_paths}": repr(_require_paths),
46-
},
36+
ctx.actions.run(
37+
inputs = [
38+
ctx.file._gemspec_template,
39+
ctx.file._gemspec_builder,
40+
metadata_file,
41+
] + file_deps,
42+
executable = ctx.attr.ruby_interpreter.files_to_run.executable,
43+
arguments = [
44+
ctx.file._gemspec_builder.path,
45+
"--output",
46+
gemspec.path,
47+
"--metadata",
48+
metadata_file.path,
49+
"--template",
50+
ctx.file._gemspec_template.path,
51+
],
52+
outputs = [gemspec],
4753
)
4854

4955
return [
@@ -66,10 +72,6 @@ _ATTRS = {
6672
"data": attr.label_list(
6773
allow_files = True,
6874
),
69-
"_gemspec_template": attr.label(
70-
allow_single_file = True,
71-
default = "gemspec_template.tpl",
72-
),
7375
"gem_name": attr.string(),
7476
"srcs": attr.label_list(
7577
allow_files = True,
@@ -79,6 +81,23 @@ _ATTRS = {
7981
allow_files = True,
8082
),
8183
"require_paths": attr.string_list(),
84+
"_gemspec_template": attr.label(
85+
allow_single_file = True,
86+
default = "gemspec_template.tpl",
87+
),
88+
"ruby_sdk": attr.string(
89+
default = "@org_ruby_lang_ruby_toolchain",
90+
),
91+
"ruby_interpreter": attr.label(
92+
default = "@org_ruby_lang_ruby_toolchain//:ruby_bin",
93+
allow_files = True,
94+
executable = True,
95+
cfg = "host",
96+
),
97+
"_gemspec_builder": attr.label(
98+
default = Label("@coinbase_rules_ruby//ruby/private/gem:gemspec_builder.rb"),
99+
allow_single_file = True,
100+
),
82101
}
83102

84103
rb_gemspec = rule(

ruby/private/gem/gemspec_builder.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# frozen_string_literal: true
2+
3+
require 'json'
4+
require 'optparse'
5+
6+
def parse_opts
7+
output_file = nil
8+
metadata_file = nil
9+
template_file = nil
10+
11+
OptionParser.new do |opts|
12+
opts.on('--template [ARG]', 'Gemspec template file') do |v|
13+
template_file = v
14+
end
15+
opts.on('--output [ARG]', 'Output file') do |v|
16+
output_file = v
17+
end
18+
opts.on('--metadata [ARG]', 'Metadata file') do |v|
19+
metadata_file = v
20+
end
21+
opts.on('-h', '--help') do |_v|
22+
puts opts
23+
exit 0
24+
end
25+
end.parse!
26+
27+
[output_file, metadata_file, template_file]
28+
end
29+
30+
def add_src_file(src, new_srcs, new_require_paths)
31+
return unless File.file?(src)
32+
33+
new_srcs << src
34+
new_require_paths << File.dirname(src)
35+
end
36+
37+
def parse_metadata_srcs(metadata)
38+
# Files and required paths can include a directory which gemspec
39+
# cannot handle. This will convert directories to individual files
40+
# and update require_paths to include them..
41+
srcs = metadata['srcs']
42+
new_require_paths = []
43+
new_srcs = []
44+
srcs.each do |src|
45+
if File.directory?(src)
46+
Dir.glob("#{src}/**/*") do |f|
47+
add_src_file(f, new_srcs, new_require_paths)
48+
end
49+
else
50+
add_src_file(src, new_srcs, new_require_paths)
51+
end
52+
end
53+
metadata['srcs'] = new_srcs
54+
metadata['require_paths'] = new_require_paths
55+
metadata
56+
end
57+
58+
def main
59+
output_file, metadata_file, template_file = parse_opts
60+
data = File.read(template_file)
61+
m = File.read(metadata_file)
62+
metadata = JSON.parse(m)
63+
64+
metadata = parse_metadata_srcs(metadata)
65+
filtered_data = data
66+
67+
metadata.each do |key, value|
68+
replace_val = "{#{key}}"
69+
filtered_data = filtered_data.gsub(replace_val, value.to_s)
70+
end
71+
72+
File.open(output_file, 'w') do |out_file|
73+
out_file.write(filtered_data)
74+
end
75+
end
76+
77+
main if $PROGRAM_NAME == __FILE__

ruby/private/gem/gemspec_template.tpl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Gem::Specification.new do |s|
2-
s.name = {name}
3-
s.summary = {name}
2+
s.name = "{name}"
3+
s.summary = "{name}"
44
s.authors = {authors}
5-
s.version = {version}
5+
s.version = "{version}"
66
s.files = {srcs}
77
s.require_paths = {require_paths}
88
end

0 commit comments

Comments
 (0)