crystalruby 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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