crystalruby 0.1.12 → 0.2.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.
@@ -1,43 +1,138 @@
1
- FAKE_ARG = "crystal"
1
+ module CrystalRuby
2
2
 
3
- alias ErrorCallback = (Pointer(UInt8), Pointer(UInt8) -> Void)
3
+ ARGV1 = "crystalruby"
4
+ CALLBACK_MUX = Mutex.new
5
+
6
+ alias ErrorCallback = (Pointer(UInt8), Pointer(UInt8), UInt32 -> Void)
4
7
 
5
- module CrystalRuby
6
8
  # Initializing Crystal Ruby invokes init on the Crystal garbage collector.
7
9
  # We need to be sure to only do this once.
8
10
  @@initialized = false
9
11
 
10
- # We won't natively handle Crystal Exceptions in Ruby
11
- # Instead, we'll catch them in Crystal, and explicitly expose them to Ruby via
12
- # the error_callback.
13
- @@error_callback
12
+ @@libname = "crystalruby"
13
+ # Our Ruby <-> Crystal Reactor uses Fibers, with callbacks to allow
14
+ # multiple concurrent Crystal operations to be queued
15
+ @@callbacks = [] of Proc(Nil)
16
+
17
+ # We only continue to yield to the Crystal scheduler from Ruby
18
+ # while there are outstanding tasks.
19
+ @@task_counter : Atomic(Int32) = Atomic.new(0)
20
+
21
+ # We can override the error callback to catch errors in Crystal,
22
+ # and explicitly expose them to Ruby.
23
+ @@error_callback : ErrorCallback = ->(t : UInt8* , s : UInt8*, tid : UInt32){ puts "Error: #{t}:#{s}" }
14
24
 
15
25
  # This is the entry point for instantiating CrystalRuby
16
26
  # We:
17
27
  # 1. Initialize the Crystal garbage collector
18
28
  # 2. Set the error callback
19
29
  # 3. Call the Crystal main function
20
- def self.init(error_callback : ErrorCallback)
30
+ def self.init(libname : Pointer(UInt8), error_callback : ErrorCallback)
21
31
  return if @@initialized
22
- GC.init
23
- @@initialized = true
32
+ @@initialized = true
33
+ argv_ptr = ARGV1.to_unsafe
34
+ Crystal.main_user_code(0, pointerof(argv_ptr))
24
35
  @@error_callback = error_callback
25
- ptr = FAKE_ARG.to_unsafe
26
- LibCrystalMain.__crystal_main(1, pointerof(ptr))
36
+ @@libname = String.new(libname)
27
37
  end
28
38
 
29
39
  # Explicit error handling (triggers exception within Ruby on the same thread)
30
- def self.report_error(error_type : String, str : String)
31
- if handler = @@error_callback
32
- handler.call(error_type.to_unsafe, str.to_unsafe)
40
+ def self.report_error(error_type : String, str : String, thread_id : UInt32, )
41
+ @@error_callback.call(error_type.to_unsafe, str.to_unsafe, thread_id)
42
+ end
43
+
44
+ def self.error_callback : ErrorCallback
45
+ @@error_callback
46
+ end
47
+
48
+ # New async task started
49
+ def self.increment_task_counter
50
+ @@task_counter.add(1)
51
+ end
52
+
53
+ # Async task finished
54
+ def self.decrement_task_counter
55
+ @@task_counter.sub(1)
56
+ end
57
+
58
+ # Get number of outstanding tasks
59
+ def self.get_task_counter : Int32
60
+ @@task_counter.get()
61
+ end
62
+
63
+ # Queue a callback for an async task
64
+ def self.queue_callback(callback : Proc(Nil))
65
+ CALLBACK_MUX.synchronize do
66
+ @@callbacks << callback
33
67
  end
34
68
  end
69
+
70
+ # Get number of queued callbacks
71
+ def self.count_callbacks : Int32
72
+ @@callbacks.size
73
+ end
74
+
75
+ def self.libname : String
76
+ @@libname
77
+ end
78
+
79
+ # Flush all callbacks
80
+ def self.flush_callbacks : Int32
81
+ CALLBACK_MUX.synchronize do
82
+ count = @@callbacks.size
83
+ @@callbacks.each do |callback|
84
+ result = callback.call()
85
+ end
86
+ @@callbacks.clear
87
+ end
88
+ get_task_counter
89
+ end
90
+ end
91
+
92
+ # Initialize CrystalRuby
93
+ fun init(libname : Pointer(UInt8), cb : CrystalRuby::ErrorCallback): Void
94
+ CrystalRuby.init(libname, cb)
95
+ end
96
+
97
+ fun stop : Void
98
+ LibGC.deinit()
99
+ end
100
+
101
+ @[Link("gc")]
102
+ lib LibGC
103
+ $stackbottom = GC_stackbottom : Void*
104
+ fun deinit = GC_deinit
35
105
  end
36
106
 
37
- fun init(cb : ErrorCallback): Void
38
- CrystalRuby.init(cb)
107
+ module GC
108
+ def self.current_thread_stack_bottom
109
+ {Pointer(Void).null, LibGC.stackbottom}
110
+ end
111
+
112
+ def self.set_stackbottom(stack_bottom : Void*)
113
+ LibGC.stackbottom = stack_bottom
114
+ end
39
115
  end
40
116
 
117
+ # Yield to the Crystal scheduler from Ruby
118
+ # If there's callbacks to process, we flush them
119
+ # Otherwise, we yield to the Crystal scheduler and let Ruby know
120
+ # how many outstanding tasks still remain (it will stop yielding to Crystal
121
+ # once this figure reaches 0).
122
+ fun yield() : Int32
123
+ if CrystalRuby.count_callbacks == 0
124
+ Fiber.yield
125
+
126
+ # TODO: We should apply backpressure here to prevent busy waiting if the number of outstanding tasks is not decreasing.
127
+ # Use a simple exponential backoff strategy, to increase the time between each yield up to a maximum of 1 second.
128
+
129
+ CrystalRuby.get_task_counter
130
+ else
131
+ CrystalRuby.flush_callbacks()
132
+ end
133
+ end
134
+
135
+
41
136
  # This is where we define all our Crystal modules and types
42
137
  # derived from their Ruby counterparts.
43
138
  %{type_modules}
@@ -19,6 +19,7 @@ module CrystalRuby
19
19
  nil
20
20
  end
21
21
  restores << [context, method_name, old_method]
22
+ context.singleton_class.undef_method(method_name) if old_method
22
23
  context.define_singleton_method(method_name) do |*args|
23
24
  Types.send(method_name, *args)
24
25
  end
@@ -26,6 +27,7 @@ module CrystalRuby
26
27
  yield
27
28
  ensure
28
29
  restores.each do |context, method_name, old_method|
30
+ context.singleton_class.undef_method(method_name)
29
31
  context.define_singleton_method(method_name, old_method) if old_method
30
32
  end
31
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CrystalRuby
2
4
  module Typemaps
3
5
  CRYSTAL_TYPE_MAP = {
@@ -52,15 +54,104 @@ module CrystalRuby
52
54
  string: '"".to_unsafe' # String type
53
55
  }
54
56
 
55
- C_TYPE_MAP = CRYSTAL_TYPE_MAP.merge({
56
- string: "UInt8*"
57
- })
57
+ C_TYPE_MAP = CRYSTAL_TYPE_MAP.merge(
58
+ {
59
+ string: "Pointer(UInt8)"
60
+ }
61
+ )
58
62
 
59
63
  C_TYPE_CONVERSIONS = {
60
64
  string: {
61
65
  from: "String.new(%s)",
62
66
  to: "%s.to_unsafe"
67
+ },
68
+ void: {
69
+ to: "nil"
63
70
  }
64
- }
71
+ }.tap do |hash|
72
+ hash.define_singleton_method(:convert) do |type, dir, expr|
73
+ if hash.key?(type)
74
+ conversion_string = hash[type][dir]
75
+ conversion_string =~ /%/ ? conversion_string % expr : conversion_string
76
+ else
77
+ expr
78
+ end
79
+ end
80
+ end
81
+
82
+ def build_type_map(crystalruby_type)
83
+ {
84
+ ffi_type: ffi_type(crystalruby_type),
85
+ ffi_ret_type: ffi_type(crystalruby_type),
86
+ crystal_type: crystal_type(crystalruby_type),
87
+ lib_type: lib_type(crystalruby_type),
88
+ error_value: error_value(crystalruby_type),
89
+ arg_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
90
+ lambda { |arg|
91
+ crystalruby_type.prepare_argument(arg)
92
+ }
93
+ end,
94
+ retval_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
95
+ lambda { |arg|
96
+ crystalruby_type.prepare_retval(arg)
97
+ }
98
+ end,
99
+ convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
100
+ convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
101
+ }
102
+ end
103
+
104
+ def ffi_type(type)
105
+ case type
106
+ when Symbol then type
107
+ when Types::TypeSerializer then type.ffi_type
108
+ end
109
+ end
110
+
111
+ def lib_type(type)
112
+ if type.is_a?(Types::TypeSerializer)
113
+ type.lib_type
114
+ else
115
+ C_TYPE_MAP.fetch(type)
116
+ end
117
+ rescue StandardError
118
+ raise "Unsupported type #{type}"
119
+ end
120
+
121
+ def error_value(type)
122
+ if type.is_a?(Types::TypeSerializer)
123
+ type.error_value
124
+ else
125
+ ERROR_VALUE.fetch(type)
126
+ end
127
+ rescue StandardError
128
+ raise "Unsupported type #{type}"
129
+ end
130
+
131
+ def crystal_type(type)
132
+ if type.is_a?(Types::TypeSerializer)
133
+ type.crystal_type
134
+ else
135
+ CRYSTAL_TYPE_MAP.fetch(type)
136
+ end
137
+ rescue StandardError
138
+ raise "Unsupported type #{type}"
139
+ end
140
+
141
+ def convert_lib_to_crystal_type(expr, type)
142
+ if type.is_a?(Types::TypeSerializer)
143
+ type.lib_to_crystal_type_expr(expr)
144
+ else
145
+ C_TYPE_CONVERSIONS.convert(type, :from, expr)
146
+ end
147
+ end
148
+
149
+ def convert_crystal_to_lib_type(expr, type)
150
+ if type.is_a?(Types::TypeSerializer)
151
+ type.crystal_to_lib_type_expr(expr)
152
+ else
153
+ C_TYPE_CONVERSIONS.convert(type, :to, expr)
154
+ end
155
+ end
65
156
  end
66
157
  end
@@ -1,9 +1,10 @@
1
- require 'json'
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
2
4
 
3
5
  module CrystalRuby::Types
4
6
  class TypeSerializer
5
7
  class JSON < TypeSerializer
6
-
7
8
  def lib_type
8
9
  "UInt8*"
9
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crystalruby
4
- VERSION = "0.1.12"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/crystalruby.rb CHANGED
@@ -13,357 +13,50 @@ require_relative "crystalruby/types"
13
13
  require_relative "crystalruby/typebuilder"
14
14
  require_relative "crystalruby/template"
15
15
  require_relative "crystalruby/compilation"
16
+ require_relative "crystalruby/adapter"
17
+ require_relative "crystalruby/reactor"
18
+ require_relative "crystalruby/library"
19
+ require_relative "crystalruby/function"
20
+ require_relative "module"
16
21
 
17
22
  module CrystalRuby
18
- CR_SRC_FILES_PATTERN = "./**/*.cr"
19
- # Define a method to set the @crystalize proc if it doesn't already exist
20
- def crystalize(type = :src, **options, &block)
21
- (args,), returns = options.first
22
- args ||= {}
23
- raise "Arguments should be of the form name: :type. Got #{args}" unless args.is_a?(Hash)
24
-
25
- @crystalize_next = { raw: type.to_sym == :raw, args: args, returns: returns, block: block }
26
- end
27
-
28
- def crystal(type = :src, &block)
29
- inline_crystal_body = Template.render(
30
- Template::InlineChunk,
31
- {
32
- module_name: name,
33
- body: block.source.lines[
34
- type == :raw ? 2...-2 : 1...-1
35
- ].join("\n")
36
- }
37
- )
38
- CrystalRuby.write_chunk(self, body: inline_crystal_body)
39
- end
40
-
41
- def crtype(&block)
42
- TypeBuilder.with_injected_type_dsl(self) do
43
- TypeBuilder.build(&block)
44
- end
45
- end
46
-
47
- def json(&block)
48
- crtype(&block).serialize_as(:json)
49
- end
50
-
51
- def method_added(method_name)
52
- if @crystalize_next
53
- attach_crystalized_method(method_name)
54
- @crystalize_next = nil
55
- end
56
- super
57
- end
58
-
59
- def config
60
- CrystalRuby.config
61
- end
62
-
63
- def attach_crystalized_method(method_name)
64
- CrystalRuby.instantiate_crystal_ruby! unless CrystalRuby.instantiated?
65
-
66
- function_body = instance_method(method_name).source.lines[
67
- @crystalize_next[:raw] ? 2...-2 : 1...-1
68
- ].join("\n")
69
-
70
- fname = "#{name.downcase}_#{method_name}"
71
- args, returns, block = @crystalize_next.values_at(:args, :returns, :block)
72
- args ||= {}
73
- @crystalize_next = nil
74
- function = build_function(self, method_name, args, returns, function_body)
75
- CrystalRuby.write_chunk(self, name: function[:name], body: function[:body]) do
76
- extend FFI::Library
77
- ffi_lib config.crystal_lib_dir / config.crystal_lib_name
78
- attach_function method_name, fname, function[:ffi_types], function[:return_ffi_type]
79
- if block
80
- [singleton_class, self].each do |receiver|
81
- receiver.prepend(Module.new do
82
- define_method(method_name, &block)
83
- end)
84
- end
85
- end
86
- end
87
-
88
- [singleton_class, self].each do |receiver|
89
- receiver.prepend(Module.new do
90
- define_method(method_name) do |*args|
91
- CrystalRuby.build! unless CrystalRuby.compiled?
92
- unless CrystalRuby.attached?
93
- CrystalRuby.attach!
94
- return send(method_name, *args) if block
95
- end
96
- args.each_with_index do |arg, i|
97
- args[i] = function[:arg_maps][i][arg] if function[:arg_maps][i]
98
- end
99
- result = super(*args)
100
- if function[:retval_map]
101
- function[:retval_map][result]
102
- else
103
- result
104
- end
105
- end
106
- end)
107
- end
108
- end
109
-
110
23
  module_function
111
24
 
112
- def build_function(owner, name, args, returns, body)
113
- arg_types = args.transform_values(&method(:build_type_map))
114
- return_type = build_type_map(returns)
115
- function_body = Template.render(
116
- Template::Function,
117
- {
118
- module_name: owner.name,
119
- lib_fn_name: "#{owner.name.downcase}_#{name}",
120
- fn_name: name,
121
- fn_body: body,
122
- fn_args: arg_types.map { |k, arg_type| "#{k} : #{arg_type[:crystal_type]}" }.join(","),
123
- fn_ret_type: return_type[:crystal_type],
124
- lib_fn_args: arg_types.map { |k, arg_type| "_#{k}: #{arg_type[:lib_type]}" }.join(","),
125
- lib_fn_ret_type: return_type[:lib_type],
126
- convert_lib_args: arg_types.map do |k, arg_type|
127
- "#{k} = #{arg_type[:convert_lib_to_crystal_type]["_#{k}"]}"
128
- end.join("\n "),
129
- arg_names: args.keys.join(","),
130
- convert_return_type: return_type[:convert_crystal_to_lib_type]["return_value"],
131
- error_value: return_type[:error_value]
132
- }
133
- )
134
- {
135
- name: name,
136
- body: function_body,
137
- retval_map: returns.is_a?(Types::TypeSerializer) ? ->(rv) { returns.prepare_retval(rv) } : nil,
138
- ffi_types: arg_types.map { |_k, arg_type| arg_type[:ffi_type] },
139
- arg_maps: arg_types.map { |_k, arg_type| arg_type[:mapper] },
140
- return_ffi_type: return_type[:return_ffi_type]
141
- }
142
- end
143
-
144
- def build_type_map(crystalruby_type)
145
- if crystalruby_type.is_a?(Types::TypeSerializer) && !crystalruby_type.anonymous?
146
- CrystalRuby.register_type!(crystalruby_type)
147
- end
148
-
149
- {
150
- ffi_type: ffi_type(crystalruby_type),
151
- return_ffi_type: ffi_type(crystalruby_type),
152
- crystal_type: crystal_type(crystalruby_type),
153
- lib_type: lib_type(crystalruby_type),
154
- error_value: error_value(crystalruby_type),
155
- mapper: crystalruby_type.is_a?(Types::TypeSerializer) ? ->(arg) { crystalruby_type.prepare_argument(arg) } : nil,
156
- convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
157
- convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
158
- }
159
- end
160
-
161
- def ffi_type(type)
162
- case type
163
- when Symbol then type
164
- when Types::TypeSerializer then type.ffi_type
165
- end
166
- end
167
-
168
- def lib_type(type)
169
- if type.is_a?(Types::TypeSerializer)
170
- type.lib_type
171
- else
172
- Typemaps::C_TYPE_MAP.fetch(type)
173
- end
174
- rescue StandardError => e
175
- raise "Unsupported type #{type}"
176
- end
177
-
178
- def error_value(type)
179
- if type.is_a?(Types::TypeSerializer)
180
- type.error_value
181
- else
182
- Typemaps::ERROR_VALUE.fetch(type)
183
- end
184
- rescue StandardError => e
185
- raise "Unsupported type #{type}"
186
- end
187
-
188
- def crystal_type(type)
189
- if type.is_a?(Types::TypeSerializer)
190
- type.crystal_type
191
- else
192
- Typemaps::CRYSTAL_TYPE_MAP.fetch(type)
193
- end
194
- rescue StandardError => e
195
- raise "Unsupported type #{type}"
196
- end
197
-
198
- def convert_lib_to_crystal_type(expr, type)
199
- if type.is_a?(Types::TypeSerializer)
200
- type.lib_to_crystal_type_expr(expr)
201
- else
202
- Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:from] % expr : expr
203
- end
204
- end
205
-
206
- def convert_crystal_to_lib_type(expr, type)
207
- if type.is_a?(Types::TypeSerializer)
208
- type.crystal_to_lib_type_expr(expr)
209
- else
210
- Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:to] % expr : expr
211
- end
212
- end
213
-
214
- def self.instantiate_crystal_ruby!
215
- unless system("which crystal > /dev/null 2>&1")
216
- raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH."
217
- end
218
-
219
- @instantiated = true
220
- %w[crystal_lib_dir crystal_main_file crystal_src_dir crystal_lib_name].each do |config_key|
221
- unless config.send(config_key)
222
- raise "Missing config option `#{config_key}`. \nProvide this inside crystalruby.yaml (run `bundle exec crystalruby init` to generate this file with detaults)"
223
- end
224
- end
225
- FileUtils.mkdir_p config.crystal_codegen_dir_abs
226
- FileUtils.mkdir_p config.crystal_lib_dir_abs
227
- FileUtils.mkdir_p config.crystal_src_dir_abs
228
- unless File.exist?(config.crystal_src_dir_abs / config.crystal_main_file)
229
- IO.write(
230
- config.crystal_src_dir_abs / config.crystal_main_file,
231
- "require \"./#{config.crystal_codegen_dir}/index\"\n"
232
- )
233
- end
234
-
235
- attach_crystal_ruby_lib! if compiled?
236
-
237
- return if File.exist?(config.crystal_src_dir / "shard.yml")
238
-
239
- IO.write("#{config.crystal_src_dir}/shard.yml", <<~YAML)
240
- name: src
241
- version: 0.1.0
242
- YAML
25
+ def initialized?
26
+ !!@initialized
243
27
  end
244
28
 
245
- def attach_crystal_ruby_lib!
246
- extend FFI::Library
247
- ffi_lib config.crystal_lib_dir / config.crystal_lib_name
248
- attach_function "init!", :init, [:pointer], :void
249
- send(:remove_const, :ErrorCallback) if defined?(ErrorCallback)
250
- const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
251
- error_type = error_type.to_sym
252
- is_exception_type = Object.const_defined?(error_type) && Object.const_get(error_type).ancestors.include?(Exception)
253
- error_type = is_exception_type ? Object.const_get(error_type) : RuntimeError
254
- raise error_type.new(message)
255
- end)
256
- init!(ErrorCallback)
257
- end
29
+ def initialize_crystal_ruby!
30
+ return if initialized?
258
31
 
259
- def self.instantiated?
260
- @instantiated
32
+ check_crystal_ruby!
33
+ check_config!
34
+ @initialized = true
261
35
  end
262
36
 
263
- def self.compiled?
264
- @compiled = get_current_crystal_lib_digest == get_cr_src_files_digest unless defined?(@compiled)
265
- @compiled
266
- end
37
+ def check_crystal_ruby!
38
+ return if system("which crystal > /dev/null 2>&1")
267
39
 
268
- def self.attached?
269
- !!@attached
40
+ raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH." \
41
+ "See https://crystal-lang.org/install/"
270
42
  end
271
43
 
272
- def self.register_type!(type)
273
- @types_cache ||= {}
274
- @types_cache[type.name] = type.type_defn
275
- end
44
+ def check_config!
45
+ return if config.crystal_src_dir
276
46
 
277
- def type_modules
278
- (@types_cache || {}).map do |type_name, expr|
279
- parts = type_name.split("::")
280
- typedef = parts[0...-1].each_with_index.reduce("") do |acc, (part, index)|
281
- acc + "#{" " * index}module #{part}\n"
282
- end
283
- typedef += "#{" " * (parts.size - 1)}alias #{parts.last} = #{expr}\n"
284
- typedef + parts[0...-1].reverse.each_with_index.reduce("") do |acc, (_part, index)|
285
- acc + "#{" " * (parts.size - 2 - index)}end\n"
286
- end
287
- end.join("\n")
47
+ raise "Missing config option `crystal_src_dir`. \nProvide this inside crystalruby.yaml "\
48
+ "(run `bundle exec crystalruby init` to generate this file with detaults)"
288
49
  end
289
50
 
290
- def self.requires
291
- chunk_store.map do |function|
292
- function_data = function[:body]
293
- file_digest = Digest::MD5.hexdigest function_data
294
- fname = function[:name]
295
- "require \"./#{function[:owner].name}/#{fname}_#{file_digest}.cr\"\n"
296
- end.join("\n")
297
- end
51
+ %w[debug info warn error].each do |level|
52
+ define_method("log_#{level}") do |*msg|
53
+ prefix = config.colorize_log_output ? "\e[33mcrystalruby\e[0m\e[90m [#{Thread.current.object_id}]\e[0m" : "[crystalruby] #{Thread.current.object_id}"
298
54
 
299
- def self.build!
300
- File.write config.crystal_codegen_dir_abs / "index.cr", Template.render(
301
- Template::Index,
302
- type_modules: type_modules,
303
- requires: requires
304
- )
305
- if @compiled = CrystalRuby::Compilation.compile!(
306
- verbose: config.verbose,
307
- debug: config.debug
308
- )
309
- IO.write(digest_file_name, get_cr_src_files_digest)
310
- attach_crystal_ruby_lib!
311
- else
312
- File.delete(digest_file_name) if File.exist?(digest_file_name)
313
- raise "Error compiling crystal code"
55
+ config.logger.send(level, "#{prefix} #{msg.join(", ")}")
314
56
  end
315
57
  end
316
58
 
317
- def self.attach!
318
- @chunk_store.each do |function|
319
- function[:compile_callback]&.call
320
- end
321
- @attached = true
322
- end
323
-
324
- def self.get_cr_src_files_digest
325
- file_digests = Dir.glob(CR_SRC_FILES_PATTERN).sort.map do |file_path|
326
- content = File.read(file_path)
327
- Digest::MD5.hexdigest(content)
328
- end.join
329
- Digest::MD5.hexdigest(file_digests)
330
- end
331
-
332
- def self.digest_file_name
333
- @digest_file_name ||= config.crystal_lib_dir_abs / "#{config.crystal_lib_name}.digest"
334
- end
335
-
336
- def self.chunk_store
337
- @chunk_store ||= []
338
- end
339
-
340
- def self.get_current_crystal_lib_digest
341
- File.read(digest_file_name) if File.exist?(digest_file_name)
342
- end
343
-
344
- def self.write_chunk(owner, body:, name: Digest::MD5.hexdigest(body), &compile_callback)
345
- chunk_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
346
- FileUtils.mkdir_p(config.crystal_codegen_dir_abs)
347
- existing = Dir.glob("#{config.crystal_codegen_dir_abs}/**/*.cr")
348
- chunk_store.each do |function|
349
- owner_name = function[:owner].name
350
- FileUtils.mkdir_p(config.crystal_codegen_dir_abs / owner_name)
351
- function_data = function[:body]
352
- fname = function[:name]
353
- file_digest = Digest::MD5.hexdigest function_data
354
- filename = config.crystal_codegen_dir_abs / owner_name / "#{fname}_#{file_digest}.cr"
355
- unless existing.delete(filename.to_s)
356
- @compiled = false
357
- @attached = false
358
- File.write(filename, function_data)
359
- end
360
- existing.select do |f|
361
- f =~ /#{config.crystal_codegen_dir / owner_name / "#{fname}_[a-f0-9]{32}\.cr"}/
362
- end.each do |fl|
363
- File.delete(fl) unless fl.eql?(filename.to_s)
364
- end
365
- end
59
+ def compile!
60
+ CrystalRuby::Library.all.each(&:build!)
366
61
  end
367
62
  end
368
-
369
- require_relative "module"
data/lib/module.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Module
2
- prepend CrystalRuby
2
+ prepend CrystalRuby::Adapter
3
3
  end