Skip to content

Commit 44ee5e3

Browse files
author
graham jenson
committed
[FEATURE] bundle_install rule publishes individual gems
1 parent 9dd48ea commit 44ee5e3

File tree

3 files changed

+148
-57
lines changed

3 files changed

+148
-57
lines changed

ruby/private/bundle/BUILD.bundle.tpl

Lines changed: 0 additions & 35 deletions
This file was deleted.

ruby/private/bundle/bundle.bzl

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,54 @@ def _get_bundler_lib_label(repository_ctx, ruby_sdk):
1515
def bundle_install_impl(ctx):
1616
ctx.symlink(ctx.attr.gemfile, "Gemfile")
1717
ctx.symlink(ctx.attr.gemfile_lock, "Gemfile.lock")
18+
ctx.symlink(ctx.attr._create_bundle_build_file, "create_bundle_build_file.rb")
1819

1920
ruby = _get_interpreter_label(ctx, ctx.attr.ruby_sdk)
2021
bundler = _get_bundler_label(ctx, ctx.attr.ruby_sdk)
2122

23+
# Install the Gems into the workspace
2224
args = [
23-
'env', '-i',
24-
ctx.path(ruby),
25-
'--disable-gems',
26-
'-I', ctx.path(bundler).dirname.dirname.get_child('lib'),
27-
ctx.path(bundler),
28-
'install',
29-
'--deployment',
30-
'--standalone',
31-
'--frozen',
32-
'--binstubs=bin',
33-
'--path=lib',
25+
'env', '-i', # remove all environment variables
26+
ctx.path(ruby), # ruby
27+
'--disable-gems', # prevent the addition of gem installation directories to the default load path
28+
'-I', ctx.path(bundler).dirname.dirname.get_child('lib'), # Used to tell Ruby where to load the library scripts
29+
ctx.path(bundler), # execute bundler
30+
'install', # install
31+
'--deployment', # In deployment mode, Bundler will 'roll-out' the bundle for production or CI use.
32+
'--standalone', # Makes a bundle that can work without depending on Rubygems or Bundler at runtime.
33+
'--frozen', # Do not allow the Gemfile.lock to be updated after this install.
34+
'--binstubs=bin', # Creates a directory and place any executables from the gem there.
35+
'--path=lib', # The location to install the specified gems to.
3436
]
3537
result = ctx.execute(args, quiet=False)
38+
3639
if result.return_code:
3740
fail("Failed to install gems: %s%s" % (result.stdout, result.stderr))
3841

42+
# exclude any specified files
3943
exclude = []
4044
for gem, globs in ctx.attr.excludes.items():
4145
expanded = ["lib/ruby/*/gems/%s-*/%s" % (gem, glob) for glob in globs]
4246
exclude.extend(expanded)
4347

44-
ctx.template(
48+
# Create the BUILD file to expose the gems to the WORKSPACE
49+
args = [
50+
'env', '-i', # remove all environment variables
51+
ctx.path(ruby), # ruby
52+
'--disable-gems', # prevent the addition of gem installation directories to the default load path
53+
'-I', ctx.path(bundler).dirname.dirname.get_child('lib'), # Used to tell Ruby where to load the library scripts
54+
'create_bundle_build_file.rb',
4555
'BUILD.bazel',
46-
ctx.attr._buildfile_template,
47-
substitutions = {
48-
"{repo_name}": ctx.name,
49-
"{exclude}": repr(exclude),
50-
"{workspace_name}": RULES_RUBY_WORKSPACE_NAME,
51-
},
52-
)
56+
'Gemfile.lock',
57+
ctx.name,
58+
repr(exclude),
59+
RULES_RUBY_WORKSPACE_NAME,
60+
61+
]
62+
result = ctx.execute(args, quiet=False)
63+
64+
if result.return_code:
65+
fail("Failed to create build file: %s%s" % (result.stdout, result.stderr))
5366

5467

5568
bundle_install = repository_rule(
@@ -68,10 +81,10 @@ bundle_install = repository_rule(
6881
doc = "List of glob patterns per gem to be excluded from the library",
6982
),
7083

71-
"_buildfile_template": attr.label(
72-
default = "%s//ruby/private/bundle:BUILD.bundle.tpl" % (
84+
"_create_bundle_build_file": attr.label(
85+
default = "%s//ruby/private/bundle:create_bundle_build_file.rb" % (
7386
RULES_RUBY_WORKSPACE_NAME),
74-
doc = "The template of BUILD files for installed gem bundles",
87+
doc = "Creates the BUILD file",
7588
allow_single_file = True,
7689
),
7790
},
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
TEMPLATE = 'load(
2+
"{workspace_name}//ruby:defs.bzl",
3+
"ruby_library",
4+
)
5+
6+
package(default_visibility = ["//visibility:public"])
7+
8+
filegroup(
9+
name = "binstubs",
10+
srcs = glob(["bin/**/*"]),
11+
data = [":libs"],
12+
)
13+
14+
ruby_library(
15+
name = "bundler_setup",
16+
srcs = ["lib/bundler/setup.rb"],
17+
visibility = ["//visibility:private"],
18+
)
19+
20+
# DEPRECATED: use :all instead
21+
ruby_library(
22+
name = "libs",
23+
srcs = glob(
24+
include = [
25+
"lib/ruby/{ruby_version}/gems/*/**/*",
26+
"lib/ruby/{ruby_version}/bin/**/*",
27+
],
28+
exclude = {exclude},
29+
),
30+
deps = [":bundler_setup"],
31+
rubyopt = ["-r../{repo_name}/lib/bundler/setup.rb"],
32+
)
33+
34+
# PULL EACH GEM INDIVIDUALLY
35+
'
36+
37+
GEM_TEMPLATE = '
38+
ruby_library(
39+
name = "{name}",
40+
srcs = glob(
41+
include = [
42+
"lib/ruby/{ruby_version}/gems/{name}-{version}/**/*",
43+
# TODO scope down included bin files
44+
"lib/ruby/{ruby_version}/bin/**/*",
45+
],
46+
exclude = {exclude},
47+
),
48+
deps = {deps},
49+
rubyopt = ["-r../{repo_name}/lib/bundler/setup.rb"],
50+
)
51+
52+
'
53+
54+
ALL_TEMPLATE = '
55+
ruby_library(
56+
name = "all",
57+
deps = {deps},
58+
rubyopt = ["-r../{repo_name}/lib/bundler/setup.rb"],
59+
)
60+
'
61+
require "bundler"
62+
63+
def create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name)
64+
# TODO: properly calculate path/ruby version here
65+
# ruby_version = RUBY_VERSION # doesnt work because verion is 2.5.5 path is 2.5.0
66+
ruby_version = "*"
67+
68+
template_out = TEMPLATE.gsub("{workspace_name}", workspace_name)
69+
.gsub("{exclude}", excludes)
70+
.gsub("{repo_name}", repo_name)
71+
.gsub("{ruby_version}", ruby_version)
72+
73+
74+
75+
# Append to the end specific gem libraries and dependencies
76+
bundle = Bundler::LockfileParser.new(Bundler.read_file(lock_file))
77+
78+
bundle.specs.each { |spec|
79+
deps = spec.dependencies.map{ |d| d.name }
80+
deps += [":bundler_setup"]
81+
template_out += GEM_TEMPLATE.gsub("{exclude}", excludes)
82+
.gsub("{name}", spec.name)
83+
.gsub("{version}", spec.version.to_s)
84+
.gsub("{deps}", deps.to_s)
85+
.gsub("{repo_name}", repo_name)
86+
.gsub("{ruby_version}", ruby_version)
87+
}
88+
89+
# Append collection of all gems
90+
template_out += ALL_TEMPLATE.gsub("{repo_name}", repo_name)
91+
.gsub("{deps}", ([":bundler_setup"] + bundle.specs.map{ |s| s.name }).to_s)
92+
93+
# Write the actual BUILD file
94+
open(build_out_file, 'w') { |f|
95+
f.puts template_out
96+
}
97+
end
98+
99+
# ruby ./create_bundle_build_file.rb "BUILD.bazel" "Gemfile.lock" "repo_name" "[]" "wsp_name"
100+
if $0 == __FILE__
101+
if ARGV.length != 5
102+
fmt.Println("BUILD FILE ARGS not 5")
103+
exit(1)
104+
end
105+
106+
build_out_file = ARGV[0]
107+
lock_file = ARGV[1]
108+
repo_name = ARGV[2]
109+
excludes = ARGV[3]
110+
workspace_name = ARGV[4]
111+
112+
create_bundle_build_file(build_out_file, lock_file, repo_name, excludes, workspace_name)
113+
end

0 commit comments

Comments
 (0)