konpeito 0.3.0 → 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: 5a2c905a6cc383d9539c3d0c1377c4dc8b033636cf8c7b85987e6acb59e4c69c
4
- data.tar.gz: 2799693d9a59c0f0b90b0d11bc43da17bd78278aa88b11d3fd0796dfbad5f39b
3
+ metadata.gz: b625a264d0c1f052c3cde2b8bcc08d7dff02f4a292f462d73e218c2fb9af7e59
4
+ data.tar.gz: 3fd87bf5f8e81ac6959d0daa8b68f6f48fbf13aedfb6adb68848d1f6e9a569c8
5
5
  SHA512:
6
- metadata.gz: 184994190193063c34ea651f672b04eeb9499e3d08da65cb3b09297ca321b2a0a645241f27fcf993871858ea17defbfdbe0674a4e0ced6298f1c4e595d36c700
7
- data.tar.gz: d8689a1392633e4ed8c29756259e6196c3571c68c4148058013b962b09ee1168fec3b6d095f29f8ae2c5aa1262bde0efa2651d4265480507044b51e5d4182c21
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
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module Konpeito
8
+ module Cache
9
+ # Manages compilation cache for `konpeito run`.
10
+ # Content-addressed: cache key is SHA256 of all inputs.
11
+ class RunCache
12
+ MANIFEST_FILE = "run_manifest.json"
13
+ DEFAULT_MAX_ENTRIES = 20
14
+
15
+ attr_reader :cache_dir
16
+
17
+ def initialize(cache_dir: ".konpeito_cache/run")
18
+ @cache_dir = File.expand_path(cache_dir)
19
+ @manifest = nil
20
+ load_manifest
21
+ end
22
+
23
+ # Compute a cache key from all compilation inputs.
24
+ def compute_cache_key(source_files:, rbs_files:, options_hash:)
25
+ digest = Digest::SHA256.new
26
+ source_files.sort.each { |f| digest.update(Digest::SHA256.file(f).hexdigest) }
27
+ rbs_files.sort.each { |f| digest.update(Digest::SHA256.file(f).hexdigest) }
28
+ digest.update(options_hash.sort.map { |k, v| "#{k}=#{v}" }.join("|"))
29
+ digest.update(Konpeito::VERSION)
30
+ digest.hexdigest
31
+ end
32
+
33
+ # Look up a cached artifact. Returns the path if it exists, nil otherwise.
34
+ def lookup(cache_key, basename)
35
+ entry = @manifest["entries"][cache_key]
36
+ return nil unless entry
37
+
38
+ artifact = artifact_path(cache_key, basename)
39
+ return nil unless File.exist?(artifact)
40
+
41
+ # Update last_used_at
42
+ entry["last_used_at"] = Time.now.iso8601
43
+ save_manifest
44
+ artifact
45
+ end
46
+
47
+ # Register a compiled artifact in the manifest.
48
+ # The artifact should already exist at artifact_dir(cache_key)/basename.
49
+ def store(cache_key, basename)
50
+ artifact = artifact_path(cache_key, basename)
51
+ return nil unless File.exist?(artifact)
52
+
53
+ @manifest["entries"][cache_key] = {
54
+ "basename" => basename,
55
+ "created_at" => Time.now.iso8601,
56
+ "last_used_at" => Time.now.iso8601
57
+ }
58
+ save_manifest
59
+ cleanup!
60
+ artifact
61
+ end
62
+
63
+ # Directory for a given cache key's artifact.
64
+ def artifact_dir(cache_key)
65
+ File.join(@cache_dir, cache_key)
66
+ end
67
+
68
+ # Full path to the artifact file.
69
+ def artifact_path(cache_key, basename)
70
+ File.join(artifact_dir(cache_key), basename)
71
+ end
72
+
73
+ # Remove all cached entries.
74
+ def clean!
75
+ FileUtils.rm_rf(@cache_dir)
76
+ FileUtils.mkdir_p(@cache_dir)
77
+ @manifest = create_empty_manifest
78
+ save_manifest
79
+ end
80
+
81
+ # Evict oldest entries beyond max_entries.
82
+ def cleanup!(max_entries: DEFAULT_MAX_ENTRIES)
83
+ entries = @manifest["entries"]
84
+ return if entries.size <= max_entries
85
+
86
+ # Sort by last_used_at ascending (oldest first)
87
+ sorted = entries.sort_by { |_k, v| v["last_used_at"] || v["created_at"] || "" }
88
+ to_remove = sorted.first(entries.size - max_entries)
89
+
90
+ to_remove.each do |key, _|
91
+ dir = artifact_dir(key)
92
+ FileUtils.rm_rf(dir) if Dir.exist?(dir)
93
+ entries.delete(key)
94
+ end
95
+
96
+ save_manifest
97
+ end
98
+
99
+ private
100
+
101
+ def load_manifest
102
+ FileUtils.mkdir_p(@cache_dir)
103
+ manifest_path = File.join(@cache_dir, MANIFEST_FILE)
104
+
105
+ if File.exist?(manifest_path)
106
+ begin
107
+ @manifest = JSON.parse(File.read(manifest_path))
108
+ # Ensure entries key exists
109
+ @manifest["entries"] ||= {}
110
+ rescue JSON::ParserError
111
+ @manifest = create_empty_manifest
112
+ end
113
+ else
114
+ @manifest = create_empty_manifest
115
+ end
116
+ end
117
+
118
+ def save_manifest
119
+ manifest_path = File.join(@cache_dir, MANIFEST_FILE)
120
+ # Write to tmp file then rename for atomicity
121
+ tmp_path = "#{manifest_path}.tmp.#{Process.pid}"
122
+ File.write(tmp_path, JSON.pretty_generate(@manifest))
123
+ File.rename(tmp_path, manifest_path)
124
+ rescue StandardError
125
+ # Best effort — don't crash if manifest write fails
126
+ FileUtils.rm_f(tmp_path) if tmp_path
127
+ end
128
+
129
+ def create_empty_manifest
130
+ {
131
+ "version" => Konpeito::VERSION,
132
+ "entries" => {}
133
+ }
134
+ end
135
+ end
136
+ end
137
+ end
@@ -4,5 +4,6 @@ module Konpeito
4
4
  module Cache
5
5
  autoload :CacheManager, "konpeito/cache/cache_manager"
6
6
  autoload :DependencyGraph, "konpeito/cache/dependency_graph"
7
+ autoload :RunCache, "konpeito/cache/run_cache"
7
8
  end
8
9
  end
@@ -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
 
@@ -102,7 +102,7 @@ module Konpeito
102
102
  ;;
103
103
  run)
104
104
  if [[ "${cur}" == -* ]]; then
105
- COMPREPLY=( $(compgen -W "--target --classpath --rbs -I --require-path --inline -v --verbose --no-color -h --help" -- "${cur}") )
105
+ COMPREPLY=( $(compgen -W "--target --classpath --rbs -I --require-path --inline --no-cache --clean-run-cache -v --verbose --no-color -h --help" -- "${cur}") )
106
106
  else
107
107
  COMPREPLY=( $(compgen -f -X '!*.rb' -- "${cur}") )
108
108
  fi
@@ -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,12 +185,14 @@ 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' \
192
192
  '--require-path[Add require search path]:path:_directories' \
193
193
  '--inline[Use inline RBS annotations]' \
194
+ '--no-cache[Force recompilation]' \
195
+ '--clean-run-cache[Clear run cache before building]' \
194
196
  '-v[Verbose output]' \
195
197
  '--verbose[Verbose output]' \
196
198
  '--no-color[Disable colored output]' \
@@ -263,7 +265,7 @@ module Konpeito
263
265
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l incremental -d 'Incremental compilation'
264
266
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l clean-cache -d 'Clear compilation cache'
265
267
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l inline -d 'Use inline RBS annotations'
266
- 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'
267
269
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l run -d 'Run after building'
268
270
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l emit-ir -d 'Emit intermediate representation'
269
271
  complete -c konpeito -n '__fish_seen_subcommand_from build' -l classpath -r -d 'JVM classpath'
@@ -274,10 +276,13 @@ module Konpeito
274
276
  complete -c konpeito -n '__fish_seen_subcommand_from build' -F -d 'Ruby source file'
275
277
 
276
278
  # run options
277
- 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'
278
280
  complete -c konpeito -n '__fish_seen_subcommand_from run' -l classpath -r -d 'JVM classpath'
279
281
  complete -c konpeito -n '__fish_seen_subcommand_from run' -l rbs -r -d 'RBS type definition file'
280
282
  complete -c konpeito -n '__fish_seen_subcommand_from run' -s I -l require-path -r -d 'Add require search path'
283
+ complete -c konpeito -n '__fish_seen_subcommand_from run' -l inline -d 'Use inline RBS annotations'
284
+ complete -c konpeito -n '__fish_seen_subcommand_from run' -l no-cache -d 'Force recompilation'
285
+ complete -c konpeito -n '__fish_seen_subcommand_from run' -l clean-run-cache -d 'Clear run cache'
281
286
  complete -c konpeito -n '__fish_seen_subcommand_from run' -s v -l verbose -d 'Verbose output'
282
287
  complete -c konpeito -n '__fish_seen_subcommand_from run' -l no-color -d 'Disable colored output'
283
288
  complete -c konpeito -n '__fish_seen_subcommand_from run' -F -d 'Ruby source file'
@@ -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