rubita 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +124 -0
- data/Rakefile +12 -0
- data/lib/rubita/transpiler.rb +321 -0
- data/lib/rubita/version.rb +5 -0
- data/lib/rubita.rb +14 -0
- data/sig/rubita.rbs +4 -0
- metadata +49 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: cc492f76a821f0b1b802564b280f4a99162f8d76855ffce00e644906003bdbe5
|
|
4
|
+
data.tar.gz: cec711f5a8c8802a40840b6d3bc5152115e84fe3e55e5d18f8a835e3184c2d2b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 676b956444b8497f5c885cb6ecb627df93ca489d7db63fbe587f3790afc92f8d185dfc2000155ab774ab9209fe3a12820c5ee3ced6ca8a7b801bf01a1a4d0d98
|
|
7
|
+
data.tar.gz: dffbb32c61c04ba730524c610aee6978157bdc0aab9725c50566c504252ec250c6f225d171d5e6ca4391f60949caeaecd9af7e4447922a4594af92e4deb41966
|
data/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Rubita
|
|
2
|
+
|
|
3
|
+
Rubita is a transpiler that converts a restricted Ruby DSL into BCC-compatible C code for eBPF programs.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Rubita enables you to write eBPF probes and kernel tracing programs using a Ruby-like syntax, which are then transpiled into BCC (Berkeley Packet Filter Compiler Collection) compatible C code. This makes it easier to write complex eBPF programs while leveraging Ruby's expressiveness.
|
|
8
|
+
|
|
9
|
+
### Supported Features
|
|
10
|
+
|
|
11
|
+
- **Map Declarations**: Define eBPF hash maps with `BPF_HASH`
|
|
12
|
+
- **Probe Definitions**: Support for `TRACEPOINT_PROBE`, `KFUNC_PROBE`, `KRETFUNC_PROBE`, and `LSM_PROBE`
|
|
13
|
+
- **Method Definitions**: Define helper functions with `def`
|
|
14
|
+
- **Field Access**: Convert Ruby dot notation (`obj.field`) to C pointer dereference (`obj->field`)
|
|
15
|
+
- **String Literals**: Support format strings with proper escaping
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add this line to your application's Gemfile:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem 'rubita', github: 'udzura/rubita'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
And then execute:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bundle install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Basic Conversion
|
|
32
|
+
|
|
33
|
+
### Hash Map Declaration
|
|
34
|
+
|
|
35
|
+
**Ruby DSL:**
|
|
36
|
+
```ruby
|
|
37
|
+
BPF_HASH :events, key: :u64, value: :u64, size: 1024
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Generated C:**
|
|
41
|
+
```c
|
|
42
|
+
BPF_HASH(events, u64, u64, 1024);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Tracepoint Probe
|
|
46
|
+
|
|
47
|
+
**Ruby DSL:**
|
|
48
|
+
```ruby
|
|
49
|
+
TRACEPOINT_PROBE :syscalls, :sys_enter_open do
|
|
50
|
+
bpf_trace_printk("open syscall\n")
|
|
51
|
+
0
|
|
52
|
+
end
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Generated C:**
|
|
56
|
+
```c
|
|
57
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_open) {
|
|
58
|
+
bpf_trace_printk("open syscall\n");
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Kernel Function Probe
|
|
64
|
+
|
|
65
|
+
**Ruby DSL:**
|
|
66
|
+
```ruby
|
|
67
|
+
KFUNC_PROBE :vfs_read do
|
|
68
|
+
bpf_trace_printk("Reading file: %d\n", args.got_bits)
|
|
69
|
+
0
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Generated C:**
|
|
74
|
+
```c
|
|
75
|
+
KFUNC_PROBE(vfs_read) {
|
|
76
|
+
bpf_trace_printk("Reading file: %d\n", args->got_bits);
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Helper Functions
|
|
82
|
+
|
|
83
|
+
**Ruby DSL:**
|
|
84
|
+
```ruby
|
|
85
|
+
def print_event(_ctx)
|
|
86
|
+
bpf_trace_printk("Event occurred\n")
|
|
87
|
+
0
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Generated C:**
|
|
92
|
+
```c
|
|
93
|
+
int print_event(void *_ctx) {
|
|
94
|
+
bpf_trace_printk("Event occurred\n");
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Usage
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
require 'rubita'
|
|
103
|
+
|
|
104
|
+
ruby_code = <<~RUBY
|
|
105
|
+
BPF_HASH :counts, key: :u64, value: :u64, size: 10
|
|
106
|
+
|
|
107
|
+
TRACEPOINT_PROBE :syscalls, :sys_enter_openat do
|
|
108
|
+
bpf_trace_printk("openat\n")
|
|
109
|
+
0
|
|
110
|
+
end
|
|
111
|
+
RUBY
|
|
112
|
+
|
|
113
|
+
c_code = Rubita.transpile(ruby_code)
|
|
114
|
+
puts c_code
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
120
|
+
|
|
121
|
+
## Contributing
|
|
122
|
+
|
|
123
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/udzura/rubita.
|
|
124
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubita
|
|
4
|
+
class Transpiler
|
|
5
|
+
def transpile(source)
|
|
6
|
+
sexp = Ripper.sexp(source)
|
|
7
|
+
raise Error, "failed to parse source" if sexp.nil?
|
|
8
|
+
|
|
9
|
+
nodes = extract_program_nodes(sexp)
|
|
10
|
+
|
|
11
|
+
converted = nodes.map do |node|
|
|
12
|
+
case node[0]
|
|
13
|
+
when :def
|
|
14
|
+
convert_definition(node)
|
|
15
|
+
when :command
|
|
16
|
+
convert_top_level_command(node)
|
|
17
|
+
when :method_add_block
|
|
18
|
+
convert_top_level_block(node)
|
|
19
|
+
else
|
|
20
|
+
raise Error, "unsupported top-level node: #{node[0]}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
converted.join("\n\n")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def extract_program_nodes(sexp)
|
|
30
|
+
return raise Error, "unexpected program structure" unless sexp[0] == :program
|
|
31
|
+
|
|
32
|
+
nodes = sexp[1]
|
|
33
|
+
return raise Error, "source must contain nodes" unless nodes.is_a?(Array) && !nodes.empty?
|
|
34
|
+
|
|
35
|
+
nodes
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def convert_top_level_command(node)
|
|
39
|
+
ident = node[1]
|
|
40
|
+
return raise Error, "unsupported command format" unless [:@ident, :@const].include?(ident&.[](0))
|
|
41
|
+
|
|
42
|
+
case ident[1]
|
|
43
|
+
when "BPF_HASH"
|
|
44
|
+
convert_hashmap_command(node)
|
|
45
|
+
else
|
|
46
|
+
raise Error, "unsupported command: #{ident[1]}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def convert_top_level_block(node)
|
|
51
|
+
call_node = node[1]
|
|
52
|
+
block_node = node[2]
|
|
53
|
+
|
|
54
|
+
return raise Error, "unsupported block call" unless call_node&.[](0) == :command
|
|
55
|
+
|
|
56
|
+
ident = call_node[1]
|
|
57
|
+
return raise Error, "unsupported block command format" unless ident&.[](0) == :@const
|
|
58
|
+
|
|
59
|
+
case ident[1]
|
|
60
|
+
when "TRACEPOINT_PROBE"
|
|
61
|
+
convert_probe_block(call_node, block_node, "TRACEPOINT_PROBE", 2)
|
|
62
|
+
when "KFUNC_PROBE"
|
|
63
|
+
convert_probe_block(call_node, block_node, "KFUNC_PROBE", 1)
|
|
64
|
+
when "KRETFUNC_PROBE"
|
|
65
|
+
convert_probe_block(call_node, block_node, "KRETFUNC_PROBE", 1)
|
|
66
|
+
when "LSM_PROBE"
|
|
67
|
+
convert_probe_block(call_node, block_node, "LSM_PROBE", 1)
|
|
68
|
+
else
|
|
69
|
+
raise Error, "unsupported block command: #{ident[1]}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def convert_probe_block(call_node, block_node, macro_name, arg_count)
|
|
74
|
+
args_add_block = call_node[2]
|
|
75
|
+
return raise Error, "unsupported #{macro_name} args" unless args_add_block&.[](0) == :args_add_block
|
|
76
|
+
|
|
77
|
+
raw_args = args_add_block[1]
|
|
78
|
+
return raise Error, "#{macro_name} requires #{arg_count} arguments" unless raw_args.is_a?(Array) && raw_args.size == arg_count
|
|
79
|
+
|
|
80
|
+
macro_args = raw_args.map { |arg| convert_symbol_literal(arg) }
|
|
81
|
+
|
|
82
|
+
return raise Error, "#{macro_name} requires do ... end block" unless block_node&.[](0) == :do_block
|
|
83
|
+
bodystmt = block_node[2]
|
|
84
|
+
statements, return_value = extract_body_statements_and_return_from_bodystmt(bodystmt)
|
|
85
|
+
|
|
86
|
+
c_lines = ["#{macro_name}(#{macro_args.join(', ')}) {"]
|
|
87
|
+
statements.each do |statement|
|
|
88
|
+
c_lines << " #{convert_statement(statement)}"
|
|
89
|
+
end
|
|
90
|
+
c_lines << " return #{return_value};"
|
|
91
|
+
c_lines << "}"
|
|
92
|
+
c_lines.join("\n")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def convert_hashmap_command(node)
|
|
96
|
+
args_add_block = node[2]
|
|
97
|
+
return raise Error, "unsupported hashmap args" unless args_add_block[0] == :args_add_block
|
|
98
|
+
|
|
99
|
+
raw_args = args_add_block[1]
|
|
100
|
+
map_name = convert_symbol_literal(raw_args[0])
|
|
101
|
+
|
|
102
|
+
options_node = raw_args[1]
|
|
103
|
+
return raise Error, "hashmap options are required" unless options_node&.[](0) == :bare_assoc_hash
|
|
104
|
+
|
|
105
|
+
options = extract_assoc_hash(options_node)
|
|
106
|
+
key_type = options["key"] || (raise Error, "hashmap key is required")
|
|
107
|
+
value_type = options["value"] || (raise Error, "hashmap value is required")
|
|
108
|
+
size = options["size"]
|
|
109
|
+
|
|
110
|
+
if size
|
|
111
|
+
"BPF_HASH(#{map_name}, #{key_type}, #{value_type}, #{size});"
|
|
112
|
+
else
|
|
113
|
+
"BPF_HASH(#{map_name}, #{key_type}, #{value_type});"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def extract_assoc_hash(node)
|
|
118
|
+
pairs = node[1]
|
|
119
|
+
return raise Error, "invalid hashmap options" unless pairs.is_a?(Array)
|
|
120
|
+
|
|
121
|
+
pairs.each_with_object({}) do |pair, acc|
|
|
122
|
+
return raise Error, "invalid hashmap option pair" unless pair[0] == :assoc_new
|
|
123
|
+
|
|
124
|
+
label_node = pair[1]
|
|
125
|
+
value_node = pair[2]
|
|
126
|
+
return raise Error, "invalid hashmap option key" unless label_node[0] == :@label
|
|
127
|
+
|
|
128
|
+
key = label_node[1].delete_suffix(":")
|
|
129
|
+
value = convert_hashmap_option_value(value_node)
|
|
130
|
+
acc[key] = value
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def convert_hashmap_option_value(node)
|
|
135
|
+
case node[0]
|
|
136
|
+
when :symbol_literal
|
|
137
|
+
convert_symbol_literal(node)
|
|
138
|
+
when :@int
|
|
139
|
+
node[1]
|
|
140
|
+
else
|
|
141
|
+
raise Error, "unsupported hashmap option value: #{node[0]}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def convert_symbol_literal(node)
|
|
146
|
+
symbol_ident = node.dig(1, 1)
|
|
147
|
+
return raise Error, "unsupported symbol literal" unless symbol_ident&.[](0) == :@ident
|
|
148
|
+
|
|
149
|
+
symbol_ident[1]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def convert_definition(def_node)
|
|
153
|
+
function_name = extract_function_name(def_node)
|
|
154
|
+
statements, return_value = extract_body_statements_and_return(def_node)
|
|
155
|
+
|
|
156
|
+
c_lines = ["int #{function_name}(void *_ctx) {"]
|
|
157
|
+
statements.each do |statement|
|
|
158
|
+
c_lines << " #{convert_statement(statement)}"
|
|
159
|
+
end
|
|
160
|
+
c_lines << " return #{return_value};"
|
|
161
|
+
c_lines << "}"
|
|
162
|
+
c_lines.join("\n")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def extract_function_name(def_node)
|
|
166
|
+
ident = def_node[1]
|
|
167
|
+
return raise Error, "method name is missing" unless ident&.[](0) == :@ident
|
|
168
|
+
|
|
169
|
+
ident[1]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def extract_body_statements_and_return(def_node)
|
|
173
|
+
bodystmt = def_node[3]
|
|
174
|
+
extract_body_statements_and_return_from_bodystmt(bodystmt)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def extract_body_statements_and_return_from_bodystmt(bodystmt)
|
|
178
|
+
stmts = bodystmt[1]
|
|
179
|
+
return raise Error, "method body is missing" unless stmts.is_a?(Array) && !stmts.empty?
|
|
180
|
+
|
|
181
|
+
return_node = stmts.last
|
|
182
|
+
return raise Error, "last expression must be integer literal" unless return_node[0] == :@int
|
|
183
|
+
|
|
184
|
+
[stmts[0...-1], return_node[1]]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def convert_statement(statement)
|
|
188
|
+
case statement[0]
|
|
189
|
+
when :method_add_arg
|
|
190
|
+
convert_method_call_statement(statement)
|
|
191
|
+
else
|
|
192
|
+
raise Error, "unsupported statement: #{statement[0]}"
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def convert_method_call_statement(statement)
|
|
197
|
+
call_target = statement[1]
|
|
198
|
+
arg_part = statement[2]
|
|
199
|
+
|
|
200
|
+
case call_target[0]
|
|
201
|
+
when :fcall
|
|
202
|
+
convert_fcall_statement(call_target, arg_part)
|
|
203
|
+
when :call
|
|
204
|
+
convert_call_statement(call_target, arg_part)
|
|
205
|
+
else
|
|
206
|
+
raise Error, "unsupported call target type: #{call_target[0]}"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def convert_fcall_statement(call_target, arg_part)
|
|
211
|
+
method_ident = call_target[1]
|
|
212
|
+
return raise Error, "unsupported method identifier" unless method_ident[0] == :@ident
|
|
213
|
+
|
|
214
|
+
args = extract_args(arg_part)
|
|
215
|
+
"#{method_ident[1]}(#{args.join(', ')});"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def convert_call_statement(call_target, arg_part)
|
|
219
|
+
receiver = call_target[1]
|
|
220
|
+
method_name_node = call_target[3]
|
|
221
|
+
|
|
222
|
+
return raise Error, "unsupported call method name" unless method_name_node&.[](0) == :@ident
|
|
223
|
+
|
|
224
|
+
method_name = method_name_node[1]
|
|
225
|
+
|
|
226
|
+
# Check if receiver is a global variable
|
|
227
|
+
if receiver&.[](0) == :var_ref && receiver[1]&.[](0) == :@gvar
|
|
228
|
+
gvar_name = receiver[1][1].delete_prefix("$")
|
|
229
|
+
args = extract_args_with_reference(arg_part)
|
|
230
|
+
"#{gvar_name}.#{method_name}(#{args.join(', ')});"
|
|
231
|
+
else
|
|
232
|
+
raise Error, "unsupported call receiver type: #{receiver&.[](0)}"
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def convert_method_call(statement)
|
|
237
|
+
call_target = statement[1]
|
|
238
|
+
arg_part = statement[2]
|
|
239
|
+
|
|
240
|
+
return raise Error, "unsupported call target" unless call_target[0] == :fcall
|
|
241
|
+
method_ident = call_target[1]
|
|
242
|
+
return raise Error, "unsupported method identifier" unless method_ident[0] == :@ident
|
|
243
|
+
|
|
244
|
+
args = extract_args(arg_part)
|
|
245
|
+
"#{method_ident[1]}(#{args.join(', ')});"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def extract_args_with_reference(arg_part)
|
|
249
|
+
return raise Error, "unsupported arg format" unless arg_part[0] == :arg_paren
|
|
250
|
+
|
|
251
|
+
args_add_block = arg_part[1]
|
|
252
|
+
return raise Error, "unsupported arg list" unless args_add_block[0] == :args_add_block
|
|
253
|
+
|
|
254
|
+
raw_args = args_add_block[1]
|
|
255
|
+
raw_args.map { |arg| convert_arg_with_reference(arg) }
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def convert_arg_with_reference(arg)
|
|
259
|
+
case arg[0]
|
|
260
|
+
when :vcall
|
|
261
|
+
arg_ident = arg[1]
|
|
262
|
+
return raise Error, "unsupported variable call" unless arg_ident&.[](0) == :@ident
|
|
263
|
+
"&#{arg_ident[1]}"
|
|
264
|
+
when :string_literal
|
|
265
|
+
string_content = arg.dig(1, 1)
|
|
266
|
+
return raise Error, "unsupported string format" unless string_content&.[](0) == :@tstring_content
|
|
267
|
+
|
|
268
|
+
escaped = escape_c_string(string_content[1])
|
|
269
|
+
%Q("#{escaped}")
|
|
270
|
+
when :call
|
|
271
|
+
convert_call_arg(arg)
|
|
272
|
+
else
|
|
273
|
+
raise Error, "unsupported argument type for reference: #{arg[0]}"
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def extract_args(arg_part)
|
|
278
|
+
return raise Error, "unsupported arg format" unless arg_part[0] == :arg_paren
|
|
279
|
+
|
|
280
|
+
args_add_block = arg_part[1]
|
|
281
|
+
return raise Error, "unsupported arg list" unless args_add_block[0] == :args_add_block
|
|
282
|
+
|
|
283
|
+
raw_args = args_add_block[1]
|
|
284
|
+
raw_args.map { |arg| convert_arg(arg) }
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def convert_arg(arg)
|
|
288
|
+
case arg[0]
|
|
289
|
+
when :string_literal
|
|
290
|
+
string_content = arg.dig(1, 1)
|
|
291
|
+
return raise Error, "unsupported string format" unless string_content&.[](0) == :@tstring_content
|
|
292
|
+
|
|
293
|
+
escaped = escape_c_string(string_content[1])
|
|
294
|
+
%Q("#{escaped}")
|
|
295
|
+
when :call
|
|
296
|
+
convert_call_arg(arg)
|
|
297
|
+
else
|
|
298
|
+
raise Error, "unsupported argument type: #{arg[0]}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def convert_call_arg(call_node)
|
|
303
|
+
receiver = call_node[1]
|
|
304
|
+
method_name_node = call_node[3]
|
|
305
|
+
|
|
306
|
+
return raise Error, "unsupported call receiver" unless receiver&.[](0) == :vcall
|
|
307
|
+
receiver_ident = receiver[1]
|
|
308
|
+
return raise Error, "unsupported receiver identifier" unless receiver_ident&.[](0) == :@ident
|
|
309
|
+
|
|
310
|
+
return raise Error, "unsupported method name" unless method_name_node&.[](0) == :@ident
|
|
311
|
+
|
|
312
|
+
receiver_name = receiver_ident[1]
|
|
313
|
+
method_name = method_name_node[1]
|
|
314
|
+
"#{receiver_name}->#{method_name}"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def escape_c_string(value)
|
|
318
|
+
value.gsub("\\", "\\\\").gsub('"', '\\"')
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
data/lib/rubita.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ripper"
|
|
4
|
+
|
|
5
|
+
require_relative "rubita/version"
|
|
6
|
+
require_relative "rubita/transpiler"
|
|
7
|
+
|
|
8
|
+
module Rubita
|
|
9
|
+
class Error < StandardError; end
|
|
10
|
+
|
|
11
|
+
def self.transpile(source)
|
|
12
|
+
Transpiler.new.transpile(source)
|
|
13
|
+
end
|
|
14
|
+
end
|
data/sig/rubita.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rubita
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Uchio Kondo
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Rubita transpiles a restricted Ruby DSL into BCC-compatible C code for
|
|
13
|
+
eBPF use cases.
|
|
14
|
+
email:
|
|
15
|
+
- udzura@udzura.jp
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- README.md
|
|
21
|
+
- Rakefile
|
|
22
|
+
- lib/rubita.rb
|
|
23
|
+
- lib/rubita/transpiler.rb
|
|
24
|
+
- lib/rubita/version.rb
|
|
25
|
+
- sig/rubita.rbs
|
|
26
|
+
homepage: https://github.com/udzura/rubita
|
|
27
|
+
licenses: []
|
|
28
|
+
metadata:
|
|
29
|
+
allowed_push_host: https://rubygems.org
|
|
30
|
+
homepage_uri: https://github.com/udzura/rubita
|
|
31
|
+
source_code_uri: https://github.com/udzura/rubita
|
|
32
|
+
rdoc_options: []
|
|
33
|
+
require_paths:
|
|
34
|
+
- lib
|
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 3.2.0
|
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
45
|
+
requirements: []
|
|
46
|
+
rubygems_version: 4.0.6
|
|
47
|
+
specification_version: 4
|
|
48
|
+
summary: Ruby to BCC-compatible C transpiler for eBPF programs
|
|
49
|
+
test_files: []
|