crystalruby 0.1.10 → 0.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +17 -0
- data/README.md +16 -1
- data/examples/adder/adder.rb +10 -0
- data/exe/crystalruby +8 -8
- data/lib/crystalruby/compilation.rb +75 -0
- data/lib/crystalruby/config.rb +43 -11
- data/lib/crystalruby/templates/index.cr +1 -0
- data/lib/crystalruby/version.rb +1 -1
- data/lib/crystalruby.rb +48 -60
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41e8370627d23725b55bb1916254b88690e93b384c94692d59d73b0cf5ceda03
|
4
|
+
data.tar.gz: 7d7192ea627331a30f6cd9ff9dcc7482ed2b1e373f115cc0e3cfa19edd5db506
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56d644c482b04bb8f5b39b6626dda93079578f3f95809294a441e0fd1707aeb71eb253da7a549c351fe448750f6dd2acfdbda3782b37e5aaebb4019332c64d0c
|
7
|
+
data.tar.gz: c529c61b9564f6d23dc551209ac7ce426d56a02424a970b57825f5e993953c376fa57afb3880db0647d581391e7d88881775cd0e2284408997fbaf4e8b37a690
|
data/Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
FROM ruby:3.3
|
2
|
+
|
3
|
+
MAINTAINER Wouter Coppieters <wc@pico.net.nz>
|
4
|
+
|
5
|
+
RUN apt-get update && apt-get install -y curl gnupg2 software-properties-common lsb-release
|
6
|
+
RUN curl -fsSL https://crystal-lang.org/install.sh | bash
|
7
|
+
WORKDIR /usr/src/app
|
8
|
+
|
9
|
+
COPY Gemfile Gemfile.lock ./
|
10
|
+
COPY crystalruby.gemspec ./
|
11
|
+
COPY lib/crystalruby/version.rb ./lib/crystalruby/version.rb
|
12
|
+
|
13
|
+
RUN bundle install
|
14
|
+
COPY . .
|
15
|
+
|
16
|
+
# Define the command to run your application
|
17
|
+
CMD ["bundle", "exec", "irb"]
|
data/README.md
CHANGED
@@ -501,7 +501,7 @@ Or install it yourself as:
|
|
501
501
|
$ gem install crystalruby
|
502
502
|
```
|
503
503
|
|
504
|
-
`crystalruby`
|
504
|
+
`crystalruby` supports some basic configuration options, which can be specified inside a crystalruby.yaml file in the root of your project.
|
505
505
|
You can run `crystalruby init` to generate a configuration file with sane defaults.
|
506
506
|
|
507
507
|
```bash
|
@@ -513,6 +513,21 @@ crystal_src_dir: "./crystalruby/src"
|
|
513
513
|
crystal_lib_dir: "./crystalruby/lib"
|
514
514
|
crystal_main_file: "main.cr"
|
515
515
|
crystal_lib_name: "crlib"
|
516
|
+
crystal_codegen_dir: "generated"
|
517
|
+
debug: true
|
518
|
+
```
|
519
|
+
|
520
|
+
Alternatively, these can be set programmatically:
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
CrystalRuby.configure do |config|
|
524
|
+
config.crystal_src_dir = "./crystalruby/src"
|
525
|
+
config.crystal_lib_dir = "./crystalruby/lib"
|
526
|
+
config.crystal_main_file = "main.cr"
|
527
|
+
config.crystal_lib_name = "crlib"
|
528
|
+
config.crystal_codegen_dir = "generated"
|
529
|
+
config.debug = true
|
530
|
+
end
|
516
531
|
```
|
517
532
|
|
518
533
|
## Development
|
data/exe/crystalruby
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "bundler/setup"
|
4
4
|
require "crystalruby"
|
5
|
-
require
|
5
|
+
require "fileutils"
|
6
6
|
|
7
7
|
# Define the actions for the commands
|
8
8
|
def init
|
@@ -14,20 +14,20 @@ def init
|
|
14
14
|
crystal_main_file: "main.cr"
|
15
15
|
crystal_lib_name: "crlib"
|
16
16
|
crystal_codegen_dir: "generated"
|
17
|
+
debug: true
|
17
18
|
YAML
|
18
19
|
|
19
20
|
# Create the file at the root of the current directory
|
20
|
-
File.write(
|
21
|
-
puts
|
21
|
+
File.write("crystalruby.yaml", yaml_content)
|
22
|
+
puts "Initialized crystalruby.yaml file with dummy content."
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
25
|
def install
|
26
26
|
Dir.chdir("#{CrystalRuby.config.crystal_src_dir}") do
|
27
|
-
if system(
|
28
|
-
puts
|
27
|
+
if system("shards update")
|
28
|
+
puts "Shards installed successfully."
|
29
29
|
else
|
30
|
-
puts
|
30
|
+
puts "Error installing shards."
|
31
31
|
end
|
32
32
|
end
|
33
33
|
clean
|
@@ -40,7 +40,7 @@ end
|
|
40
40
|
|
41
41
|
def build
|
42
42
|
# This is a stub for the build command
|
43
|
-
puts
|
43
|
+
puts "Build command is not implemented yet."
|
44
44
|
end
|
45
45
|
|
46
46
|
# Main program
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
module CrystalRuby
|
5
|
+
module Compilation
|
6
|
+
def self.compile!(
|
7
|
+
src: config.crystal_src_dir_abs / config.crystal_main_file,
|
8
|
+
lib: config.crystal_lib_dir_abs / config.crystal_lib_name,
|
9
|
+
verbose: config.verbose,
|
10
|
+
debug: config.debug
|
11
|
+
)
|
12
|
+
Dir.chdir(config.crystal_src_dir_abs) do
|
13
|
+
compile_command = compile_command!(verbose: verbose, debug: debug, lib: lib, src: src)
|
14
|
+
link_command = link_cmd!(verbose: verbose, lib: lib, src: src)
|
15
|
+
|
16
|
+
puts "[crystalruby] Compiling Crystal code: #{compile_command}" if verbose
|
17
|
+
unless system(compile_command)
|
18
|
+
puts "Failed to build Crystal object file."
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "[crystalruby] Linking Crystal code: #{link_command}" if verbose
|
23
|
+
unless system(link_command)
|
24
|
+
puts "Failed to link Crystal library."
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.compile_command!(verbose:, debug:, lib:, src:)
|
33
|
+
@compile_command ||= begin
|
34
|
+
verbose_flag = verbose ? "--verbose" : ""
|
35
|
+
debug_flag = debug ? "" : "--release --no-debug"
|
36
|
+
redirect_output = " > /dev/null " unless verbose
|
37
|
+
|
38
|
+
%(crystal build #{verbose_flag} #{debug_flag} --cross-compile -o #{lib} #{src}#{redirect_output})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Here we misuse the crystal compiler to build a valid linking command
|
43
|
+
# with all of the platform specific flags that we need.
|
44
|
+
# We then use this command to link the object file that we compiled in the previous step.
|
45
|
+
# This is not robust and is likely to need revision in the future.
|
46
|
+
def self.link_cmd!(verbose:, lib:, src:)
|
47
|
+
@link_cmd ||= begin
|
48
|
+
result = nil
|
49
|
+
|
50
|
+
Dir.mktmpdir do |tmp|
|
51
|
+
output, status = Open3.capture2("crystal build --verbose #{src} -o #{Pathname.new(tmp) / "main"}")
|
52
|
+
unless status.success?
|
53
|
+
puts "Failed to compile the Crystal code."
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parse the output to find the last invocation of the C compiler, which is likely the linking stage
|
58
|
+
# and strip off the targets that the crystal compiler added.
|
59
|
+
link_command_suffix = output.lines.select { |line| line.strip.start_with?("cc") }.last.strip[/.*(-o.*)/, 1]
|
60
|
+
|
61
|
+
# Replace the output file with the path to the object file we compiled
|
62
|
+
link_command_suffix.gsub!(
|
63
|
+
/-o.*main/,
|
64
|
+
"-o #{lib}"
|
65
|
+
)
|
66
|
+
result = %(cc #{lib}.o -shared #{link_command_suffix})
|
67
|
+
result << " > /dev/null 2>&1" unless verbose
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
result
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/crystalruby/config.rb
CHANGED
@@ -9,25 +9,57 @@ module CrystalRuby
|
|
9
9
|
# Define a nested Config class
|
10
10
|
class Config
|
11
11
|
include Singleton
|
12
|
-
attr_accessor :debug, :
|
12
|
+
attr_accessor :debug, :verbose
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@debug = true
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
@paths_cache = {}
|
17
|
+
config = File.exist?("crystalruby.yaml") && begin
|
18
|
+
YAML.safe_load(IO.read("crystalruby.yaml"))
|
19
|
+
rescue StandardError
|
20
|
+
nil
|
21
|
+
end || {}
|
22
|
+
@crystal_src_dir = config.fetch("crystal_src_dir", "./crystalruby/src")
|
23
|
+
@crystal_lib_dir = config.fetch("crystal_lib_dir", "./crystalruby/lib")
|
24
|
+
@crystal_main_file = config.fetch("crystal_main_file", "main.cr")
|
25
|
+
@crystal_lib_name = config.fetch("crystal_lib_name", "crlib")
|
26
|
+
@crystal_codegen_dir = config.fetch("crystal_codegen_dir", "generated")
|
27
|
+
@crystal_project_root = config.fetch("crystal_project_root", Pathname.pwd)
|
28
|
+
@debug = config.fetch("debug", true)
|
29
|
+
@verbose = config.fetch("verbose", false)
|
30
|
+
end
|
31
|
+
|
32
|
+
%w[crystal_main_file crystal_lib_name crystal_project_root].each do |method_name|
|
33
|
+
define_method(method_name) do
|
34
|
+
@paths_cache[method_name] ||= Pathname.new(instance_variable_get(:"@#{method_name}"))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
%w[crystal_codegen_dir].each do |method_name|
|
39
|
+
abs_method_name = "#{method_name}_abs"
|
40
|
+
define_method(abs_method_name) do
|
41
|
+
@paths_cache[abs_method_name] ||= crystal_src_dir_abs / instance_variable_get(:"@#{method_name}")
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method(method_name) do
|
45
|
+
@paths_cache[method_name] ||= Pathname.new instance_variable_get(:"@#{method_name}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
%w[crystal_src_dir crystal_lib_dir].each do |method_name|
|
50
|
+
abs_method_name = "#{method_name}_abs"
|
51
|
+
define_method(abs_method_name) do
|
52
|
+
@paths_cache[abs_method_name] ||= crystal_project_root / instance_variable_get(:"@#{method_name}")
|
53
|
+
end
|
54
|
+
|
55
|
+
define_method(method_name) do
|
56
|
+
@paths_cache[method_name] ||= Pathname.new instance_variable_get(:"@#{method_name}")
|
20
57
|
end
|
21
|
-
@crystal_src_dir = config.fetch("crystal_src_dir", "./crystalruby/src")
|
22
|
-
@crystal_lib_dir = config.fetch("crystal_lib_dir", "./crystalruby/lib")
|
23
|
-
@crystal_main_file = config.fetch("crystal_main_file", "main.cr")
|
24
|
-
@crystal_lib_name = config.fetch("crystal_lib_name", "crlib")
|
25
|
-
@crystal_codegen_dir = config.fetch("crystal_codegen_dir", "generated")
|
26
58
|
end
|
27
59
|
end
|
28
60
|
|
29
61
|
def self.configure
|
30
|
-
setup
|
31
62
|
yield(config)
|
63
|
+
@paths_cache = {}
|
32
64
|
end
|
33
65
|
end
|
data/lib/crystalruby/version.rb
CHANGED
data/lib/crystalruby.rb
CHANGED
@@ -4,12 +4,15 @@ require "ffi"
|
|
4
4
|
require "digest"
|
5
5
|
require "fileutils"
|
6
6
|
require "method_source"
|
7
|
+
require "pathname"
|
8
|
+
|
7
9
|
require_relative "crystalruby/config"
|
8
10
|
require_relative "crystalruby/version"
|
9
11
|
require_relative "crystalruby/typemaps"
|
10
12
|
require_relative "crystalruby/types"
|
11
13
|
require_relative "crystalruby/typebuilder"
|
12
14
|
require_relative "crystalruby/template"
|
15
|
+
require_relative "crystalruby/compilation"
|
13
16
|
|
14
17
|
module CrystalRuby
|
15
18
|
CR_SRC_FILES_PATTERN = "./**/*.cr"
|
@@ -71,7 +74,7 @@ module CrystalRuby
|
|
71
74
|
function = build_function(self, method_name, args, returns, function_body)
|
72
75
|
CrystalRuby.write_chunk(self, name: function[:name], body: function[:body]) do
|
73
76
|
extend FFI::Library
|
74
|
-
ffi_lib
|
77
|
+
ffi_lib config.crystal_lib_dir / config.crystal_lib_name
|
75
78
|
attach_function method_name, fname, function[:ffi_types], function[:return_ffi_type]
|
76
79
|
if block
|
77
80
|
[singleton_class, self].each do |receiver|
|
@@ -85,7 +88,7 @@ module CrystalRuby
|
|
85
88
|
[singleton_class, self].each do |receiver|
|
86
89
|
receiver.prepend(Module.new do
|
87
90
|
define_method(method_name) do |*args|
|
88
|
-
CrystalRuby.
|
91
|
+
CrystalRuby.build! unless CrystalRuby.compiled?
|
89
92
|
unless CrystalRuby.attached?
|
90
93
|
CrystalRuby.attach!
|
91
94
|
return send(method_name, *args) if block
|
@@ -219,28 +222,29 @@ module CrystalRuby
|
|
219
222
|
raise "Missing config option `#{config_key}`. \nProvide this inside crystalruby.yaml (run `bundle exec crystalruby init` to generate this file with detaults)"
|
220
223
|
end
|
221
224
|
end
|
222
|
-
FileUtils.mkdir_p
|
223
|
-
FileUtils.mkdir_p
|
224
|
-
|
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)
|
225
229
|
IO.write(
|
226
|
-
|
230
|
+
config.crystal_src_dir_abs / config.crystal_main_file,
|
227
231
|
"require \"./#{config.crystal_codegen_dir}/index\"\n"
|
228
232
|
)
|
229
233
|
end
|
230
234
|
|
231
235
|
attach_crystal_ruby_lib! if compiled?
|
232
236
|
|
233
|
-
return if File.exist?(
|
237
|
+
return if File.exist?(config.crystal_src_dir / "shard.yml")
|
234
238
|
|
235
|
-
IO.write("#{config.crystal_src_dir}/shard.yml", <<~
|
239
|
+
IO.write("#{config.crystal_src_dir}/shard.yml", <<~YAML)
|
236
240
|
name: src
|
237
241
|
version: 0.1.0
|
238
|
-
|
242
|
+
YAML
|
239
243
|
end
|
240
244
|
|
241
245
|
def attach_crystal_ruby_lib!
|
242
246
|
extend FFI::Library
|
243
|
-
ffi_lib
|
247
|
+
ffi_lib config.crystal_lib_dir / config.crystal_lib_name
|
244
248
|
attach_function "init!", :init, [:pointer], :void
|
245
249
|
send(:remove_const, :ErrorCallback) if defined?(ErrorCallback)
|
246
250
|
const_set(:ErrorCallback, FFI::Function.new(:void, %i[string string]) do |error_type, message|
|
@@ -272,24 +276,19 @@ module CrystalRuby
|
|
272
276
|
|
273
277
|
def type_modules
|
274
278
|
(@types_cache || {}).map do |type_name, expr|
|
275
|
-
typedef = ""
|
276
279
|
parts = type_name.split("::")
|
277
|
-
|
278
|
-
|
279
|
-
typedef << "#{indent} module #{part}\n"
|
280
|
-
indent += " "
|
280
|
+
typedef = parts[0...-1].each_with_index.reduce("") do |acc, (part, index)|
|
281
|
+
acc + "#{" " * index}module #{part}\n"
|
281
282
|
end
|
282
|
-
typedef
|
283
|
-
parts[0...-1].
|
284
|
-
|
285
|
-
typedef << "#{indent} end\n"
|
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
286
|
end
|
287
|
-
typedef
|
288
287
|
end.join("\n")
|
289
288
|
end
|
290
289
|
|
291
290
|
def self.requires
|
292
|
-
|
291
|
+
chunk_store.map do |function|
|
293
292
|
function_data = function[:body]
|
294
293
|
file_digest = Digest::MD5.hexdigest function_data
|
295
294
|
fname = function[:name]
|
@@ -297,40 +296,26 @@ module CrystalRuby
|
|
297
296
|
end.join("\n")
|
298
297
|
end
|
299
298
|
|
300
|
-
def self.
|
301
|
-
|
302
|
-
|
303
|
-
index_content = Template.render(
|
299
|
+
def self.build!
|
300
|
+
File.write config.crystal_codegen_dir_abs / "index.cr", Template.render(
|
304
301
|
Template::Index,
|
305
|
-
|
306
|
-
|
307
|
-
requires: requires
|
308
|
-
}
|
302
|
+
type_modules: type_modules,
|
303
|
+
requires: requires
|
309
304
|
)
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
end
|
320
|
-
|
321
|
-
unless result = system(cmd)
|
322
|
-
File.delete(digest_file_name) if File.exist?(digest_file_name)
|
323
|
-
raise "Error compiling crystal code"
|
324
|
-
end
|
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"
|
325
314
|
end
|
326
|
-
|
327
|
-
IO.write(digest_file_name, get_cr_src_files_digest)
|
328
|
-
@compiled = true
|
329
|
-
attach_crystal_ruby_lib!
|
330
315
|
end
|
331
316
|
|
332
317
|
def self.attach!
|
333
|
-
@
|
318
|
+
@chunk_store.each do |function|
|
334
319
|
function[:compile_callback]&.call
|
335
320
|
end
|
336
321
|
@attached = true
|
@@ -345,7 +330,11 @@ module CrystalRuby
|
|
345
330
|
end
|
346
331
|
|
347
332
|
def self.digest_file_name
|
348
|
-
@digest_file_name ||= "#{config.
|
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 ||= []
|
349
338
|
end
|
350
339
|
|
351
340
|
def self.get_current_crystal_lib_digest
|
@@ -353,26 +342,25 @@ module CrystalRuby
|
|
353
342
|
end
|
354
343
|
|
355
344
|
def self.write_chunk(owner, body:, name: Digest::MD5.hexdigest(body), &compile_callback)
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
@block_store.each do |function|
|
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|
|
361
349
|
owner_name = function[:owner].name
|
362
|
-
FileUtils.mkdir_p(
|
350
|
+
FileUtils.mkdir_p(config.crystal_codegen_dir_abs / owner_name)
|
363
351
|
function_data = function[:body]
|
364
352
|
fname = function[:name]
|
365
353
|
file_digest = Digest::MD5.hexdigest function_data
|
366
|
-
filename =
|
367
|
-
unless existing.delete(filename)
|
354
|
+
filename = config.crystal_codegen_dir_abs / owner_name / "#{fname}_#{file_digest}.cr"
|
355
|
+
unless existing.delete(filename.to_s)
|
368
356
|
@compiled = false
|
369
357
|
@attached = false
|
370
358
|
File.write(filename, function_data)
|
371
359
|
end
|
372
360
|
existing.select do |f|
|
373
|
-
f =~
|
361
|
+
f =~ /#{config.crystal_codegen_dir / owner_name / "#{fname}_[a-f0-9]{32}\.cr"}/
|
374
362
|
end.each do |fl|
|
375
|
-
File.delete(fl) unless fl.eql?(filename)
|
363
|
+
File.delete(fl) unless fl.eql?(filename.to_s)
|
376
364
|
end
|
377
365
|
end
|
378
366
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crystalruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: digest
|
@@ -77,12 +77,15 @@ files:
|
|
77
77
|
- ".rubocop.yml"
|
78
78
|
- CHANGELOG.md
|
79
79
|
- CODE_OF_CONDUCT.md
|
80
|
+
- Dockerfile
|
80
81
|
- LICENSE.txt
|
81
82
|
- README.md
|
82
83
|
- Rakefile
|
83
84
|
- crystalruby.gemspec
|
85
|
+
- examples/adder/adder.rb
|
84
86
|
- exe/crystalruby
|
85
87
|
- lib/crystalruby.rb
|
88
|
+
- lib/crystalruby/compilation.rb
|
86
89
|
- lib/crystalruby/config.rb
|
87
90
|
- lib/crystalruby/template.rb
|
88
91
|
- lib/crystalruby/templates/function.cr
|