-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathruby_runtime.bzl
More file actions
167 lines (140 loc) · 5.53 KB
/
ruby_runtime.bzl
File metadata and controls
167 lines (140 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
load("//ruby/private:constants.bzl", "RULES_RUBY_WORKSPACE_NAME")
load("//ruby/private/toolchains:repository_context.bzl", "ruby_repository_context")
def _install_ruby_version(ctx, version):
ctx.download_and_extract(
url = "https://github.com/rbenv/ruby-build/archive/refs/tags/v20220218.tar.gz",
sha256 = "35c82b13b7bc3713eee5615b0145c79fbbac32873f55f2ab796620d76970d8e3",
stripPrefix = "ruby-build-20220218",
)
install_path = "./build"
ctx.execute(
["./bin/ruby-build", "--verbose", version, install_path],
quiet = False,
timeout = 1600, # massive timeout because this does a lot and is a beast
)
def _is_subpath(path, ancestors):
"""Determines if path is a subdirectory of one of the ancestors"""
for ancestor in ancestors:
if not ancestor.endswith("/"):
ancestor += "/"
if path.startswith(ancestor):
return True
return False
def _relativate(path):
if not path:
return path
# Assuming that absolute paths start with "/".
# TODO(yugui) support windows
if path.startswith("/"):
return path[1:]
elif path.startswith("C:/"):
return path[3:]
else:
return path
def _list_libdirs(ruby):
"""List the LOAD_PATH of the ruby"""
paths = ruby.eval(ruby, "print $:.join(\"\\n\")")
paths = sorted(paths.split("\n"))
rel_paths = [_relativate(path) for path in paths]
return (paths, rel_paths)
def _install_dirs(ctx, ruby, *names):
paths = sorted([ruby.rbconfig(ruby, name) for name in names])
# JRuby reports some of the directories as nulls.
paths = [path for path in paths if path]
# Sometimes we end up with the same directory multiple times
# so make sure paths are unique by converting it to set.
# For example, this is what we have on Fedora 34:
# $ ruby -rrbconfig -e "p RbConfig::CONFIG['rubyhdrdir']"
# "/usr/include"
# $ ruby -rrbconfig -e "p RbConfig::CONFIG['rubyarchhdrdir']"
# "/usr/include"
paths = depset(paths).to_list()
rel_paths = [_relativate(path) for path in paths]
for i, (path, rel_path) in enumerate(zip(paths, rel_paths)):
if not _is_subpath(path, paths[:i]):
ctx.symlink(path, rel_path)
return rel_paths
def _install_ruby(ctx, ruby):
# Places SDK
ctx.symlink(ruby.interpreter_realpath, ruby.rel_interpreter_path)
# Places the interpreter at a predictable place regardless of the actual binary name
# so that ruby_bundle can depend on it.
ctx.template(
"ruby",
ctx.attr._interpreter_wrapper_template,
substitutions = {
"{workspace_name}": ctx.name,
"{rel_interpreter_path}": ruby.rel_interpreter_path,
},
)
# Install libruby
static_library = ruby.expand_rbconfig(ruby, "${libdir}/${LIBRUBY_A}")
if ctx.path(static_library).exists:
ctx.symlink(static_library, _relativate(static_library))
else:
static_library = None
shared_library = ruby.expand_rbconfig(ruby, "${libdir}/${LIBRUBY_SO}")
if ctx.path(shared_library).exists:
ctx.symlink(shared_library, _relativate(shared_library))
else:
shared_library = None
return struct(
includedirs = _install_dirs(ctx, ruby, "rubyarchhdrdir", "rubyhdrdir"),
static_library = _relativate(static_library),
shared_library = _relativate(shared_library),
)
def host_ruby_is_correct_version(ctx, version):
interpreter_path = ctx.which("ruby")
if not interpreter_path:
print("Can't find ruby interpreter in the PATH")
return False
ruby_version = ctx.execute(["ruby", "-e", "print RUBY_VERSION"]).stdout
have_ruby_version = (version == ruby_version)
if have_ruby_version:
print("Found local Ruby SDK version '%s' which matches requested version '%s'" % (ruby_version, version))
return have_ruby_version
def _ruby_runtime_impl(ctx):
# If the current version of ruby is correct use that
version = ctx.attr.version
if version == "host" or host_ruby_is_correct_version(ctx, version):
interpreter_path = ctx.which("ruby")
else:
_install_ruby_version(ctx, version)
interpreter_path = ctx.path("./build/bin/ruby")
if not interpreter_path:
fail(
"Command 'ruby' not found. Set $PATH or specify interpreter_path",
"interpreter_path",
)
ruby = ruby_repository_context(ctx, interpreter_path)
installed = _install_ruby(ctx, ruby)
ctx.template(
"BUILD.bazel",
ctx.attr._buildfile_template,
substitutions = {
"{includes}": repr(installed.includedirs),
"{hdrs}": repr(["%s/**/*.h" % path for path in installed.includedirs]),
"{static_library}": repr(installed.static_library),
"{shared_library}": repr(installed.shared_library),
"{rules_ruby_workspace}": RULES_RUBY_WORKSPACE_NAME,
},
executable = False,
)
ruby_runtime = repository_rule(
implementation = _ruby_runtime_impl,
attrs = {
"version": attr.string(default = "host"),
"_buildfile_template": attr.label(
default = "%s//ruby/private/toolchains:BUILD.runtime.tpl" % (
RULES_RUBY_WORKSPACE_NAME
),
allow_single_file = True,
),
"_interpreter_wrapper_template": attr.label(
default = "%s//ruby/private/toolchains:interpreter_wrapper.tpl" % (
RULES_RUBY_WORKSPACE_NAME
),
allow_single_file = True,
),
},
)