konpeito 0.3.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fe9ef9f2b988dc2d9751cecb6f53c2828e1e0d412cecc897557d7988e967b48
4
- data.tar.gz: 68b6e211a60e31b48b61858b62902229b0980e786147b2e4427688f169c0caea
3
+ metadata.gz: b625a264d0c1f052c3cde2b8bcc08d7dff02f4a292f462d73e218c2fb9af7e59
4
+ data.tar.gz: 3fd87bf5f8e81ac6959d0daa8b68f6f48fbf13aedfb6adb68848d1f6e9a569c8
5
5
  SHA512:
6
- metadata.gz: 9dbf1f9e6edffb5e9e91af5398620fa96218f2ddde0cddebfa31ab4b90a42c0206cc71d24a6e2035dfd34f3695b7ba5265aef606c91af1b3cdee430609f537de
7
- data.tar.gz: 7e595a3ec330e9f359bd5dc412b7d3c9fe9e9a3bef0fde8632f7b35fbe4f9902b174a93a25b11a5ea1e5364983f4ec8d1a92ff3e78a0aa5ebeeaf015bf2dd821
6
+ metadata.gz: 5372d88aaca539699920771d4a8dce41589ee8074910a65f2170e71fb7e32775cfc9d397230ee83148587a8665093367c1c264138bf53c30ff1924ce4d6aa724
7
+ data.tar.gz: 3381123783ab44d4cc6ebfacc0aaa7afc829fe4b97c2d5e222866bda75ec181f3a76c03a0430538e93411adbc86b5e5c899c606afab8b842b78a4af026485ca4
data/CHANGELOG.md CHANGED
@@ -5,6 +5,41 @@ All notable changes to Konpeito will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-03-08
9
+
10
+ ### Added
11
+ - **mruby backend**: standalone executable generation (`konpeito build --target mruby`)
12
+ - CRuby-compatible C wrappers (`mruby_helpers.c`) — same LLVM IR for both runtimes
13
+ - Block/yield, Proc, Fiber, exception handling support
14
+ - Static linking for FFI libraries
15
+ - Compilation caching for `konpeito run --target mruby`
16
+ - **Cross-compilation**: `--cross` + `--cross-mruby` + zig cc for targeting other platforms
17
+ - **raylib stdlib**: zero-boilerplate game development for mruby backend
18
+ - 87 cfunc bindings (window, drawing, text, keyboard, mouse, colors, keys, random)
19
+ - Auto-detected when `module Raylib` is referenced — no manual setup needed
20
+ - **Game examples**: Breakout and catch game demos (`examples/mruby_breakout/`, `examples/mruby_stdlib_demo/`)
21
+ - Documentation: mruby backend, raylib stdlib, and cross-compilation guides
22
+
23
+ ### Fixed
24
+ - MergedAST handling in stdlib auto-detection
25
+ - Float/double ABI mismatch in mruby runtime
26
+ - mruby truthiness check compatibility
27
+ - Skip stdlib tests when native extension build fails on CI
28
+
29
+ ## [0.3.1] - 2026-03-07
30
+
31
+ ### Added
32
+ - Compilation caching for `konpeito run` — cached artifacts in `.konpeito_cache/run/`
33
+ - Jekyll/GitHub Pages documentation site with just-the-docs theme
34
+
35
+ ### Fixed
36
+ - GitHub Pages baseurl resolution for step IDs
37
+
38
+ ### Changed
39
+ - Documentation restructured: Getting Started now leads with CRuby backend
40
+ - Removed speculative performance claims and inaccurate benchmark numbers from docs
41
+ - Language specification: removed benchmark numbers, fixed Known Limitations
42
+
8
43
  ## [0.3.0] - 2026-03-05
9
44
 
10
45
  ### Added
@@ -212,6 +247,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
212
247
  - `%a{extern}` - external C struct wrappers
213
248
  - `%a{simd}` - SIMD vectorization
214
249
 
250
+ [0.4.0]: https://github.com/i2y/konpeito/compare/v0.3.1...v0.4.0
251
+ [0.3.1]: https://github.com/i2y/konpeito/compare/v0.3.0...v0.3.1
215
252
  [0.3.0]: https://github.com/i2y/konpeito/compare/v0.2.4...v0.3.0
216
253
  [0.2.4]: https://github.com/i2y/konpeito/compare/v0.2.3...v0.2.4
217
254
  [0.2.3]: https://github.com/i2y/konpeito/compare/v0.2.2...v0.2.3
@@ -171,6 +171,8 @@ module Konpeito
171
171
 
172
172
  if target == :jvm
173
173
  "#{base}.jar"
174
+ elsif target == :mruby
175
+ Platform.windows? ? "#{base}.exe" : base
174
176
  else
175
177
  case format
176
178
  when :cruby_ext
@@ -94,7 +94,7 @@ module Konpeito
94
94
  options[:inline_rbs] = true
95
95
  end
96
96
 
97
- opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
97
+ opts.on("--target TARGET", %i[native jvm mruby], "Target platform (native, jvm, mruby)") do |target|
98
98
  options[:target] = target
99
99
  end
100
100
 
@@ -114,6 +114,18 @@ module Konpeito
114
114
  options[:library] = true
115
115
  end
116
116
 
117
+ opts.on("--cross TARGET", "Cross-compile for target triple (e.g., x86_64-linux-gnu)") do |target|
118
+ options[:cross_target] = target
119
+ end
120
+
121
+ opts.on("--cross-mruby DIR", "Path to cross-compiled mruby (include/ and lib/)") do |dir|
122
+ options[:cross_mruby_dir] = dir
123
+ end
124
+
125
+ opts.on("--cross-libs DIR", "Additional library search path for cross-compilation") do |dir|
126
+ options[:cross_libs_dir] = dir
127
+ end
128
+
117
129
  opts.on("--stats", "Show optimization statistics") do
118
130
  options[:stats] = true
119
131
  end
@@ -154,7 +166,11 @@ module Konpeito
154
166
  end
155
167
  end
156
168
 
157
- target_label = options[:target] == :jvm ? "jvm" : "native"
169
+ target_label = case options[:target]
170
+ when :jvm then "jvm"
171
+ when :mruby then "mruby"
172
+ else "native"
173
+ end
158
174
  emit("Compiling", "#{source_file} (#{target_label})") unless options[:quiet]
159
175
 
160
176
  compiler = Compiler.new(
@@ -173,7 +189,10 @@ module Konpeito
173
189
  run_after: options[:run_after],
174
190
  emit_ir: options[:emit_ir],
175
191
  classpath: classpath,
176
- library: options[:library]
192
+ library: options[:library],
193
+ cross_target: options[:cross_target],
194
+ cross_mruby_dir: options[:cross_mruby_dir],
195
+ cross_libs_dir: options[:cross_libs_dir]
177
196
  )
178
197
 
179
198
  compiler.compile
@@ -210,8 +229,11 @@ module Konpeito
210
229
 
211
230
  # Show usage hint (skip for --run and --lib)
212
231
  unless options[:run_after] || options[:library]
213
- if options[:target] == :jvm
232
+ case options[:target]
233
+ when :jvm
214
234
  emit("Hint", "java -jar #{output_file}")
235
+ when :mruby
236
+ emit("Hint", "./#{output_file}")
215
237
  else
216
238
  ext_name = File.basename(output_file, File.extname(output_file))
217
239
  emit("Hint", "ruby -r ./#{ext_name} -e \"YourModule.method\"")
@@ -60,7 +60,7 @@ module Konpeito
60
60
  --no-color -h --help
61
61
  ].freeze
62
62
 
63
- TARGET_VALUES = %w[native jvm].freeze
63
+ TARGET_VALUES = %w[native jvm mruby].freeze
64
64
 
65
65
  def bash_completion
66
66
  <<~'BASH'
@@ -81,7 +81,7 @@ module Konpeito
81
81
 
82
82
  # Complete --target values
83
83
  if [[ "${prev}" == "--target" ]]; then
84
- COMPREPLY=( $(compgen -W "native jvm" -- "${cur}") )
84
+ COMPREPLY=( $(compgen -W "native jvm mruby" -- "${cur}") )
85
85
  return 0
86
86
  fi
87
87
 
@@ -170,7 +170,7 @@ module Konpeito
170
170
  '--incremental[Enable incremental compilation]' \
171
171
  '--clean-cache[Clear compilation cache]' \
172
172
  '--inline[Use inline RBS annotations]' \
173
- '--target[Target platform]:target:(native jvm)' \
173
+ '--target[Target platform]:target:(native jvm mruby)' \
174
174
  '--run[Run after building]' \
175
175
  '--emit-ir[Emit intermediate representation]' \
176
176
  '--classpath[JVM classpath]:path:_files' \
@@ -185,7 +185,7 @@ module Konpeito
185
185
  ;;
186
186
  run)
187
187
  _arguments \
188
- '--target[Target platform]:target:(native jvm)' \
188
+ '--target[Target platform]:target:(native jvm mruby)' \
189
189
  '--classpath[JVM classpath]:path:_files' \
190
190
  '--rbs[RBS type definition file]:file:_files -g "*.rbs"' \
191
191
  '-I[Add require search path]:path:_directories' \
@@ -265,7 +265,7 @@ module Konpeito
265
265
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l incremental -d 'Incremental compilation'
266
266
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l clean-cache -d 'Clear compilation cache'
267
267
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l inline -d 'Use inline RBS annotations'
268
- complete -c konpeito -n '__fish_seen_subcommand_from build' -l target -r -a 'native jvm' -d 'Target platform'
268
+ complete -c konpeito -n '__fish_seen_subcommand_from build' -l target -r -a 'native jvm mruby' -d 'Target platform'
269
269
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l run -d 'Run after building'
270
270
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l emit-ir -d 'Emit intermediate representation'
271
271
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l classpath -r -d 'JVM classpath'
@@ -276,7 +276,7 @@ module Konpeito
276
276
  complete -c konpeito -n '__fish_seen_subcommand_from build' -F -d 'Ruby source file'
277
277
 
278
278
  # run options
279
- complete -c konpeito -n '__fish_seen_subcommand_from run' -l target -r -a 'native jvm' -d 'Target platform'
279
+ complete -c konpeito -n '__fish_seen_subcommand_from run' -l target -r -a 'native jvm mruby' -d 'Target platform'
280
280
  complete -c konpeito -n '__fish_seen_subcommand_from run' -l classpath -r -d 'JVM classpath'
281
281
  complete -c konpeito -n '__fish_seen_subcommand_from run' -l rbs -r -d 'RBS type definition file'
282
282
  complete -c konpeito -n '__fish_seen_subcommand_from run' -s I -l require-path -r -d 'Add require search path'
@@ -22,6 +22,7 @@ module Konpeito
22
22
  checks.concat(core_checks)
23
23
  checks.concat(native_checks) if check_native?
24
24
  checks.concat(jvm_checks) if check_jvm?
25
+ checks.concat(mruby_checks) if check_mruby?
25
26
  checks.concat(ui_checks) if check_ui?
26
27
  checks.concat(optional_checks)
27
28
 
@@ -56,7 +57,7 @@ module Konpeito
56
57
  end
57
58
 
58
59
  def setup_option_parser(opts)
59
- opts.on("--target TARGET", %i[native jvm ui], "Check only native, jvm, or ui dependencies") do |target|
60
+ opts.on("--target TARGET", %i[native jvm mruby ui], "Check only native, jvm, mruby, or ui dependencies") do |target|
60
61
  options[:target] = target
61
62
  end
62
63
 
@@ -77,6 +78,10 @@ module Konpeito
77
78
  options[:target].nil? || options[:target] == :jvm
78
79
  end
79
80
 
81
+ def check_mruby?
82
+ options[:target] == :mruby
83
+ end
84
+
80
85
  def check_ui?
81
86
  options[:target] == :ui
82
87
  end
@@ -172,6 +177,65 @@ module Konpeito
172
177
  checks
173
178
  end
174
179
 
180
+ def mruby_checks
181
+ checks = []
182
+
183
+ # mruby-config
184
+ mruby_config = Platform.find_mruby_config
185
+ checks << {
186
+ name: "mruby-config",
187
+ detail: mruby_config || "not found",
188
+ status: mruby_config ? :ok : :warning,
189
+ hint: "Install: #{Platform.mruby_install_hint}"
190
+ }
191
+
192
+ # mruby headers
193
+ cflags = Platform.mruby_cflags
194
+ inc_dir = cflags.split.find { |f| f.start_with?("-I") }&.sub(/^-I/, "")
195
+ header_found = inc_dir && File.exist?(File.join(inc_dir, "mruby.h"))
196
+ checks << {
197
+ name: "mruby.h",
198
+ detail: header_found ? inc_dir : "not found",
199
+ status: header_found ? :ok : :missing,
200
+ hint: "Install mruby development headers. #{Platform.mruby_install_hint}"
201
+ }
202
+
203
+ # libmruby
204
+ ldflags = Platform.mruby_ldflags
205
+ lib_dir = ldflags.split.find { |f| f.start_with?("-L") }&.sub(/^-L/, "")
206
+ lib_found = if lib_dir
207
+ File.exist?(File.join(lib_dir, "libmruby.a")) || File.exist?(File.join(lib_dir, "libmruby.so"))
208
+ else
209
+ false
210
+ end
211
+ checks << {
212
+ name: "libmruby",
213
+ detail: lib_found ? lib_dir : "not found (will try system default)",
214
+ status: lib_found ? :ok : :warning,
215
+ hint: "Install: #{Platform.mruby_install_hint}"
216
+ }
217
+
218
+ # clang (also needed for mruby backend)
219
+ clang_path = Platform.find_llvm_tool("clang")
220
+ checks << {
221
+ name: "clang",
222
+ detail: clang_path || "not found",
223
+ status: clang_path ? :ok : :missing,
224
+ hint: "Install: #{Platform.llvm_install_hint}"
225
+ }
226
+
227
+ # zig (optional, for cross-compilation)
228
+ zig_path = Platform.find_zig
229
+ checks << {
230
+ name: "zig (cross-compile)",
231
+ detail: zig_path || "not found (optional)",
232
+ status: zig_path ? :ok : :info,
233
+ hint: "Install for cross-compilation: https://ziglang.org/download/"
234
+ }
235
+
236
+ checks
237
+ end
238
+
175
239
  def optional_checks
176
240
  checks = []
177
241
 
@@ -58,7 +58,7 @@ module Konpeito
58
58
  options[:with_git] = false
59
59
  end
60
60
 
61
- opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
61
+ opts.on("--target TARGET", %i[native jvm mruby], "Target platform (native, jvm, mruby)") do |target|
62
62
  options[:target] = target
63
63
  end
64
64
 
@@ -33,6 +33,8 @@ module Konpeito
33
33
  case target
34
34
  when :jvm
35
35
  run_jvm(source_file)
36
+ when :mruby
37
+ run_mruby(source_file)
36
38
  else
37
39
  run_native(source_file)
38
40
  end
@@ -56,7 +58,7 @@ module Konpeito
56
58
  end
57
59
 
58
60
  def setup_option_parser(opts)
59
- opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
61
+ opts.on("--target TARGET", %i[native jvm mruby], "Target platform (native, jvm, mruby)") do |target|
60
62
  options[:target] = target
61
63
  end
62
64
 
@@ -225,6 +227,116 @@ module Konpeito
225
227
  build_args
226
228
  end
227
229
 
230
+ def run_mruby(source_file)
231
+ require "konpeito/cache"
232
+
233
+ base = File.basename(source_file, ".rb")
234
+ basename = Platform.windows? ? "#{base}.exe" : base
235
+ run_cache = Cache::RunCache.new(cache_dir: ".konpeito_cache/run_mruby")
236
+
237
+ if options[:clean_run_cache]
238
+ run_cache.clean!
239
+ emit("Cleaned", "mruby run cache")
240
+ end
241
+
242
+ if options[:no_cache]
243
+ build_and_run_mruby_tmpdir(source_file, basename)
244
+ return
245
+ end
246
+
247
+ cache_key = compute_mruby_run_cache_key(source_file, run_cache)
248
+
249
+ if cache_key
250
+ artifact = run_cache.lookup(cache_key, basename)
251
+ if artifact
252
+ emit("Cached", source_file)
253
+ emit("Running", artifact)
254
+ system(artifact)
255
+ return
256
+ end
257
+ end
258
+
259
+ if cache_key
260
+ build_and_run_mruby_cached(source_file, run_cache, cache_key, basename)
261
+ else
262
+ build_and_run_mruby_tmpdir(source_file, basename)
263
+ end
264
+ end
265
+
266
+ def compute_mruby_run_cache_key(source_file, run_cache)
267
+ resolver = DependencyResolver.new(
268
+ base_paths: options[:require_paths],
269
+ verbose: false
270
+ )
271
+ resolver.resolve(source_file)
272
+
273
+ all_sources = resolver.resolved_files.keys
274
+ auto_rbs = resolver.rbs_paths
275
+ all_rbs = (options[:rbs_paths].map { |p| File.expand_path(p) } + auto_rbs).uniq
276
+ all_rbs = all_rbs.select { |f| File.exist?(f) }
277
+
278
+ # Include extra C files in cache key (they affect the binary)
279
+ source_dir = File.dirname(File.expand_path(source_file))
280
+ extra_c_files = Dir.glob(File.join(source_dir, "*.c")).sort
281
+
282
+ options_hash = {
283
+ "inline_rbs" => options[:inline_rbs].to_s,
284
+ "target" => "mruby"
285
+ }
286
+
287
+ digest = Digest::SHA256.new
288
+ all_sources.sort.each { |f| digest.update(Digest::SHA256.file(f).hexdigest) }
289
+ all_rbs.sort.each { |f| digest.update(Digest::SHA256.file(f).hexdigest) }
290
+ extra_c_files.each { |f| digest.update(Digest::SHA256.file(f).hexdigest) }
291
+ digest.update(options_hash.sort.map { |k, v| "#{k}=#{v}" }.join("|"))
292
+ digest.update(Konpeito::VERSION)
293
+ digest.hexdigest
294
+ rescue StandardError => e
295
+ puts_verbose("Cache key computation failed: #{e.message}")
296
+ nil
297
+ end
298
+
299
+ def build_and_run_mruby_cached(source_file, run_cache, cache_key, basename)
300
+ dir = run_cache.artifact_dir(cache_key)
301
+ FileUtils.mkdir_p(dir)
302
+ output_file = File.join(dir, basename)
303
+
304
+ build_args = build_mruby_args(source_file, output_file)
305
+ Commands::BuildCommand.new(build_args, config: config).run
306
+
307
+ run_cache.store(cache_key, basename)
308
+
309
+ emit("Running", output_file)
310
+ system(output_file)
311
+ end
312
+
313
+ def build_and_run_mruby_tmpdir(source_file, basename)
314
+ require "tmpdir"
315
+
316
+ @run_tmpdir = File.join(Dir.tmpdir, "konpeito_mruby_run_#{Process.pid}")
317
+ FileUtils.mkdir_p(@run_tmpdir)
318
+ output_file = File.join(@run_tmpdir, basename)
319
+
320
+ build_args = build_mruby_args(source_file, output_file)
321
+ Commands::BuildCommand.new(build_args, config: config).run
322
+
323
+ emit("Running", output_file)
324
+ system(output_file)
325
+ ensure
326
+ FileUtils.rm_rf(@run_tmpdir) if @run_tmpdir && Dir.exist?(@run_tmpdir)
327
+ end
328
+
329
+ def build_mruby_args(source_file, output_file)
330
+ build_args = ["--target", "mruby", "-o", output_file, "-q"]
331
+ build_args << "-v" if options[:verbose]
332
+ build_args << "--no-color" unless options[:color]
333
+ build_args << "--inline" if options[:inline_rbs]
334
+ options[:rbs_paths].each { |p| build_args << "--rbs" << p }
335
+ options[:require_paths].each { |p| build_args << "-I" << p }
336
+ build_args << source_file
337
+ build_args
338
+ end
339
+
228
340
  def build_classpath
229
341
  parts = []
230
342
 
@@ -61,7 +61,7 @@ module Konpeito
61
61
  options[:compile_first] = true
62
62
  end
63
63
 
64
- opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
64
+ opts.on("--target TARGET", %i[native jvm mruby], "Target platform (native, jvm, mruby)") do |target|
65
65
  options[:target] = target
66
66
  end
67
67