kompo 0.3.2 → 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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
4
- require 'shellwords'
3
+ require "open3"
4
+ require "shellwords"
5
5
 
6
6
  module Kompo
7
7
  # Section to compile the final binary.
@@ -19,14 +19,14 @@ module Kompo
19
19
  private
20
20
 
21
21
  def get_ruby_cflags(ruby_install_dir)
22
- ruby_pc = File.join(ruby_install_dir, 'lib', 'pkgconfig', 'ruby.pc')
23
- output, = Open3.capture2('pkg-config', '--cflags', ruby_pc, err: File::NULL)
22
+ ruby_pc = File.join(ruby_install_dir, "lib", "pkgconfig", "ruby.pc")
23
+ output, = Open3.capture2("pkg-config", "--cflags", ruby_pc, err: File::NULL)
24
24
  Shellwords.split(output.chomp)
25
25
  end
26
26
 
27
27
  def get_ruby_mainlibs(ruby_install_dir)
28
- ruby_pc = File.join(ruby_install_dir, 'lib', 'pkgconfig', 'ruby.pc')
29
- output, = Open3.capture2('pkg-config', '--variable=MAINLIBS', ruby_pc, err: File::NULL)
28
+ ruby_pc = File.join(ruby_install_dir, "lib", "pkgconfig", "ruby.pc")
29
+ output, = Open3.capture2("pkg-config", "--variable=MAINLIBS", ruby_pc, err: File::NULL)
30
30
  output.chomp
31
31
  end
32
32
 
@@ -36,9 +36,9 @@ module Kompo
36
36
  content = File.read(makefile)
37
37
  ldflags = content.scan(/^ldflags\s+= (.*)/).flatten
38
38
  ldflags += content.scan(/^LDFLAGS\s+= (.*)/).flatten
39
- ldflags.flat_map { |f| f.split(' ') }
39
+ ldflags.flat_map { |f| f.split(" ") }
40
40
  end
41
- flags.uniq.select { |f| f.start_with?('-L') }
41
+ flags.uniq.select { |f| f.start_with?("-L") }
42
42
  end
43
43
 
44
44
  def get_libpath(work_dir, ruby_major_minor)
@@ -46,23 +46,28 @@ module Kompo
46
46
  makefiles.flat_map do |makefile|
47
47
  content = File.read(makefile)
48
48
  content.scan(/^LIBPATH = (.*)/).flatten
49
- end.compact.flat_map { |p| p.split(' ') }.uniq
50
- .reject { |p| p.start_with?('-Wl,-rpath,') || !p.start_with?('-L/') }
49
+ end.compact.flat_map { |p| p.split(" ") }.uniq
50
+ .reject { |p| p.start_with?("-Wl,-rpath,") || !p.start_with?("-L/") }
51
51
  end
52
52
 
53
53
  def get_extlibs(ruby_build_path, ruby_version)
54
- exts_mk_files = Dir.glob(File.join(ruby_build_path, "ruby-#{ruby_version}", 'ext', '**', 'exts.mk'))
55
- exts_mk_files.flat_map do |file|
56
- File.read(file).scan(/^EXTLIBS\s+= (.*)/).flatten
57
- end.compact.flat_map { |l| l.split(' ') }.uniq
54
+ ruby_build_dir = File.join(ruby_build_path, "ruby-#{ruby_version}")
55
+
56
+ # Extract LIBS from ext/*/Makefile and .bundle/gems/*/ext/*/Makefile
57
+ makefiles = Dir.glob(File.join(ruby_build_dir, "{ext/*,.bundle/gems/*/ext/*}", "Makefile"))
58
+ makefiles.flat_map do |file|
59
+ # Read file, collapse line continuations, then match both "LIBS =" and "LIBS +="
60
+ content = File.read(file).gsub("\\\n", " ")
61
+ content.scan(/^LIBS\s*\+?=\s*(.*)/).flatten
62
+ end.compact.flat_map { |l| l.split }.uniq
58
63
  end
59
64
 
60
65
  def get_gem_libs(work_dir, ruby_major_minor)
61
66
  makefiles = Dir.glob("#{work_dir}/bundle/ruby/#{ruby_major_minor}.0/gems/*/ext/*/Makefile")
62
67
  makefiles.flat_map do |makefile|
63
68
  File.read(makefile).scan(/^LIBS = (.*)/).flatten
64
- end.compact.flat_map { |l| l.split(' ') }.uniq
65
- .map { |l| l.start_with?('-l') ? l : "-l#{File.basename(l, '.a').delete_prefix('lib')}" }
69
+ end.compact.flat_map { |l| l.split(" ") }.uniq
70
+ .map { |l| l.start_with?("-l") ? l : "-l#{File.basename(l, ".a").delete_prefix("lib")}" }
66
71
  end
67
72
  end
68
73
 
@@ -84,8 +89,13 @@ module Kompo
84
89
 
85
90
  command = build_command(work_dir, deps, ext_paths, enc_files)
86
91
 
87
- group('Compiling binary (macOS)') do
88
- system(*command) or raise 'Failed to compile final binary'
92
+ if Taski.args[:dry_run]
93
+ Taski.message(Shellwords.join(command))
94
+ return
95
+ end
96
+
97
+ group("Compiling binary (macOS)") do
98
+ system(*command) or raise "Failed to compile final binary"
89
99
  puts "Binary size: #{File.size(@output_path) / 1024 / 1024} MB"
90
100
  end
91
101
 
@@ -98,8 +108,8 @@ module Kompo
98
108
  ruby_static_lib = "-lruby.#{deps.ruby_major_minor}-static"
99
109
 
100
110
  [
101
- 'clang',
102
- '-O3',
111
+ "clang",
112
+ "-O3",
103
113
  get_ruby_cflags(deps.ruby_install_dir),
104
114
  # IMPORTANT: kompo_lib must come FIRST to override Homebrew-installed versions
105
115
  "-L#{deps.kompo_lib}",
@@ -110,19 +120,19 @@ module Kompo
110
120
  # Add library paths for dependencies (Homebrew on macOS)
111
121
  Shellwords.split(deps.deps_lib_paths),
112
122
  get_libpath(work_dir, deps.ruby_major_minor),
113
- '-fstack-protector-strong',
114
- '-Wl,-dead_strip', # Remove unused code/data
115
- '-Wl,-no_deduplicate', # Allow duplicate symbols from Ruby YJIT and kompo-vfs
116
- '-Wl,-export_dynamic', # Export symbols to dynamic symbol table
123
+ "-fstack-protector-strong",
124
+ "-Wl,-dead_strip", # Remove unused code/data
125
+ "-Wl,-no_deduplicate", # Allow duplicate symbols from Ruby YJIT and kompo-vfs
126
+ "-Wl,-export_dynamic", # Export symbols to dynamic symbol table
117
127
  deps.main_c,
118
128
  deps.fs_c,
119
129
  # Link kompo_wrap FIRST (before Ruby) to override libc symbols
120
- '-lkompo_wrap',
130
+ "-lkompo_wrap",
121
131
  ext_paths,
122
132
  enc_files,
123
133
  ruby_static_lib,
124
134
  get_libs(deps.ruby_install_dir, work_dir, deps.ruby_build_path, deps.ruby_version, deps.ruby_major_minor),
125
- '-o', @output_path
135
+ "-o", @output_path
126
136
  ].flatten
127
137
  end
128
138
 
@@ -131,20 +141,20 @@ module Kompo
131
141
  ruby_std_gem_libs = get_extlibs(ruby_build_path, ruby_version)
132
142
  gem_libs = get_gem_libs(work_dir, ruby_major_minor)
133
143
 
134
- all_libs = [main_libs.split(' '), gem_libs, ruby_std_gem_libs].flatten
135
- .select { |l| l.match?(/-l\w/) }.uniq
136
- .reject { |l| l == '-ldl' } # macOS doesn't have libdl
144
+ all_libs = [main_libs.split(" "), gem_libs, ruby_std_gem_libs].flatten
145
+ .select { |l| l.match?(/-l\w/) }.uniq
146
+ .reject { |l| l == "-ldl" } # macOS doesn't have libdl
137
147
 
138
148
  # Separate system libs from other libs
139
149
  other_libs = all_libs.reject { |l| SYSTEM_LIBS.any? { |sys| l == "-l#{sys}" } }
140
150
 
141
151
  [
142
152
  other_libs,
143
- '-lkompo_fs',
153
+ "-lkompo_fs",
144
154
  # System libraries
145
155
  SYSTEM_LIBS.map { |l| "-l#{l}" },
146
156
  # Frameworks
147
- FRAMEWORKS.flat_map { |f| ['-framework', f] }
157
+ FRAMEWORKS.flat_map { |f| ["-framework", f] }
148
158
  ].flatten
149
159
  end
150
160
  end
@@ -165,8 +175,13 @@ module Kompo
165
175
 
166
176
  command = build_command(work_dir, deps, ext_paths, enc_files)
167
177
 
168
- group('Compiling binary (Linux)') do
169
- system(*command) or raise 'Failed to compile final binary'
178
+ if Taski.args[:dry_run]
179
+ Taski.message(Shellwords.join(command))
180
+ return
181
+ end
182
+
183
+ group("Compiling binary (Linux)") do
184
+ system(*command) or raise "Failed to compile final binary"
170
185
  puts "Binary size: #{File.size(@output_path) / 1024 / 1024} MB"
171
186
  end
172
187
 
@@ -177,12 +192,12 @@ module Kompo
177
192
 
178
193
  def build_command(work_dir, deps, ext_paths, enc_files)
179
194
  # Linux uses libruby-static.a (not libruby.X.Y-static.a like macOS)
180
- ruby_static_lib = '-lruby-static'
195
+ ruby_static_lib = "-lruby-static"
181
196
 
182
197
  [
183
- 'gcc',
184
- '-O3',
185
- '-no-pie', # Required: Rust std lib is not built with PIC
198
+ "gcc",
199
+ "-O3",
200
+ "-no-pie", # Required: Rust std lib is not built with PIC
186
201
  get_ruby_cflags(deps.ruby_install_dir),
187
202
  # IMPORTANT: kompo_lib must come FIRST to override system-installed versions
188
203
  "-L#{deps.kompo_lib}",
@@ -193,17 +208,17 @@ module Kompo
193
208
  # Add library paths for dependencies (from pkg-config)
194
209
  Shellwords.split(deps.deps_lib_paths),
195
210
  get_libpath(work_dir, deps.ruby_major_minor),
196
- '-fstack-protector-strong',
197
- '-rdynamic', '-Wl,-export-dynamic',
211
+ "-fstack-protector-strong",
212
+ "-rdynamic", "-Wl,-export-dynamic",
198
213
  deps.main_c,
199
214
  deps.fs_c,
200
- '-Wl,-Bstatic',
201
- '-Wl,--start-group',
215
+ "-Wl,-Bstatic",
216
+ "-Wl,--start-group",
202
217
  ext_paths,
203
218
  enc_files,
204
219
  ruby_static_lib,
205
220
  get_libs(deps.ruby_install_dir, work_dir, deps.ruby_build_path, deps.ruby_version, deps.ruby_major_minor),
206
- '-o', @output_path
221
+ "-o", @output_path
207
222
  ].flatten
208
223
  end
209
224
 
@@ -214,22 +229,22 @@ module Kompo
214
229
 
215
230
  dyn_link_libs = DYN_LINK_LIBS.map { |l| "-l#{l}" }
216
231
 
217
- all_libs = [main_libs.split(' '), gem_libs, ruby_std_gem_libs].flatten
218
- .select { |l| l.match?(/-l\w/) }.uniq
232
+ all_libs = [main_libs.split(" "), gem_libs, ruby_std_gem_libs].flatten
233
+ .select { |l| l.match?(/-l\w/) }.uniq
219
234
 
220
235
  static_libs, dyn_libs = all_libs.partition { |l| !dyn_link_libs.include?(l) }
221
236
 
222
- dyn_libs << '-lc'
223
- dyn_libs.unshift('-Wl,-Bdynamic')
237
+ dyn_libs << "-lc"
238
+ dyn_libs.unshift("-Wl,-Bdynamic")
224
239
 
225
- [static_libs, '-Wl,--end-group', '-lkompo_fs', '-lkompo_wrap', dyn_libs].flatten
240
+ [static_libs, "-Wl,--end-group", "-lkompo_fs", "-lkompo_wrap", dyn_libs].flatten
226
241
  end
227
242
  end
228
243
 
229
244
  private
230
245
 
231
246
  def macos?
232
- RUBY_PLATFORM.include?('darwin')
247
+ RUBY_PLATFORM.include?("darwin")
233
248
  end
234
249
  end
235
250
  end
@@ -1,45 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require "open3"
4
4
 
5
5
  module Kompo
6
6
  # Section to get the ruby-build path.
7
- # Switches implementation based on whether ruby-build is already installed.
7
+ # Priority:
8
+ # 1. Existing ruby-build installation (via which)
9
+ # 2. macOS: Install via Homebrew
10
+ # 3. Linux: Install via git clone
8
11
  class RubyBuildPath < Taski::Section
9
12
  interfaces :path
10
13
 
11
14
  def impl
12
- ruby_build_installed? ? Installed : Install
15
+ return Installed if ruby_build_installed?
16
+
17
+ if darwin?
18
+ check_homebrew_available!
19
+ return FromHomebrew
20
+ end
21
+
22
+ FromSource
13
23
  end
14
24
 
15
25
  # Use existing ruby-build installation
16
26
  class Installed < Taski::Task
17
27
  def run
18
- path_output, = Open3.capture2('which', 'ruby-build', err: File::NULL)
28
+ path_output, = Open3.capture2("which", "ruby-build", err: File::NULL)
19
29
  @path = path_output.chomp
20
30
  puts "ruby-build path: #{@path}"
21
- version_output, = Open3.capture2(@path, '--version', err: File::NULL)
31
+ version_output, = Open3.capture2(@path, "--version", err: File::NULL)
22
32
  puts "ruby-build version: #{version_output.chomp}"
23
33
  end
24
34
  end
25
35
 
26
- # Install ruby-build via git clone and return the path
27
- class Install < Taski::Task
36
+ # Install ruby-build via Homebrew (macOS)
37
+ class FromHomebrew < Taski::Task
28
38
  def run
29
- puts 'ruby-build not found. Installing via git...'
30
- install_dir = File.expand_path('~/.ruby-build')
39
+ brew = HomebrewPath.path
40
+ puts "Installing ruby-build via Homebrew..."
41
+ system(brew, "install", "ruby-build") or raise "Failed to install ruby-build"
42
+
43
+ prefix_output, = Open3.capture2(brew, "--prefix", "ruby-build", err: File::NULL)
44
+ @path = prefix_output.chomp + "/bin/ruby-build"
45
+ raise "Failed to install ruby-build" unless File.executable?(@path)
46
+
47
+ puts "ruby-build path: #{@path}"
48
+ version_output, = Open3.capture2(@path, "--version", err: File::NULL)
49
+ puts "ruby-build version: #{version_output.chomp}"
50
+ end
51
+ end
52
+
53
+ # Install ruby-build via git clone (Linux)
54
+ class FromSource < Taski::Task
55
+ def run
56
+ puts "ruby-build not found. Installing via git..."
57
+ install_dir = File.expand_path("~/.ruby-build")
31
58
 
32
59
  if Dir.exist?(install_dir)
33
- system('git', '-C', install_dir, 'pull', '--quiet')
60
+ system("git", "-C", install_dir, "pull", "--quiet")
34
61
  else
35
- system('git', 'clone', 'https://github.com/rbenv/ruby-build.git', install_dir)
62
+ system("git", "clone", "https://github.com/rbenv/ruby-build.git", install_dir)
36
63
  end
37
64
 
38
- @path = File.join(install_dir, 'bin', 'ruby-build')
39
- raise 'Failed to install ruby-build' unless File.executable?(@path)
65
+ @path = File.join(install_dir, "bin", "ruby-build")
66
+ raise "Failed to install ruby-build" unless File.executable?(@path)
40
67
 
41
68
  puts "ruby-build installed at: #{@path}"
42
- version_output, = Open3.capture2(@path, '--version', err: File::NULL)
69
+ version_output, = Open3.capture2(@path, "--version", err: File::NULL)
43
70
  puts "ruby-build version: #{version_output.chomp}"
44
71
  end
45
72
  end
@@ -47,8 +74,26 @@ module Kompo
47
74
  private
48
75
 
49
76
  def ruby_build_installed?
50
- _, status = Open3.capture2('which', 'ruby-build', err: File::NULL)
77
+ _, status = Open3.capture2("which", "ruby-build", err: File::NULL)
51
78
  status.success?
52
79
  end
80
+
81
+ def darwin?
82
+ RUBY_PLATFORM.include?("darwin") || `uname -s`.chomp == "Darwin"
83
+ end
84
+
85
+ def check_homebrew_available!
86
+ # Check if brew is in PATH
87
+ brew_in_path, = Open3.capture2("which", "brew", err: File::NULL)
88
+ return unless brew_in_path.chomp.empty?
89
+
90
+ # Check common Homebrew installation paths (including ARM64 at /opt/homebrew)
91
+ return if HomebrewPath::COMMON_BREW_PATHS.any? { |p| File.executable?(p) }
92
+
93
+ raise <<~ERROR
94
+ Homebrew is required on macOS but not installed.
95
+ Please install Homebrew first: https://brew.sh
96
+ ERROR
97
+ end
53
98
  end
54
99
  end
data/lib/kompo/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kompo
4
- VERSION = '0.3.2'
5
- KOMPO_VFS_MIN_VERSION = '0.5.0'
4
+ VERSION = "0.4.0"
5
+ KOMPO_VFS_MIN_VERSION = "0.5.1"
6
6
  end
data/lib/kompo.rb CHANGED
@@ -1,52 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'taski'
4
- require_relative 'kompo/version'
3
+ require "taski"
4
+ require_relative "kompo/version"
5
+
6
+ # Use simple progress mode for cleaner output
7
+ # See taski library for details: https://github.com/ahogappa/taski
8
+ Taski.progress_mode = :simple
5
9
 
6
10
  module Kompo
7
11
  # Fixed path prefix for Ruby installation
8
12
  # Using a fixed path ensures that cached Ruby binaries work correctly,
9
13
  # since Ruby has hardcoded paths for standard library locations.
10
- KOMPO_PREFIX = '/kompo'
14
+ KOMPO_PREFIX = "/kompo"
11
15
 
12
16
  # Utility classes
13
- autoload :KompoIgnore, 'kompo/kompo_ignore'
17
+ autoload :KompoIgnore, "kompo/kompo_ignore"
14
18
 
15
19
  # Struct to hold file data for embedding (used by MakeFsC)
16
- autoload :KompoFile, 'kompo/tasks/make_fs_c'
20
+ autoload :KompoFile, "kompo/tasks/make_fs_c"
17
21
 
18
22
  # Core tasks
19
- autoload :WorkDir, 'kompo/tasks/work_dir'
20
- autoload :CopyProjectFiles, 'kompo/tasks/copy_project_files'
21
- autoload :CopyGemfile, 'kompo/tasks/copy_gemfile'
22
- autoload :MakeMainC, 'kompo/tasks/make_main_c'
23
- autoload :MakeFsC, 'kompo/tasks/make_fs_c'
23
+ autoload :WorkDir, "kompo/tasks/work_dir"
24
+ autoload :CopyProjectFiles, "kompo/tasks/copy_project_files"
25
+ autoload :CopyGemfile, "kompo/tasks/copy_gemfile"
26
+ autoload :MakeMainC, "kompo/tasks/make_main_c"
27
+ autoload :MakeFsC, "kompo/tasks/make_fs_c"
24
28
 
25
29
  # Ruby installation
26
- autoload :InstallRuby, 'kompo/tasks/install_ruby'
27
- autoload :CheckStdlibs, 'kompo/tasks/check_stdlibs'
30
+ autoload :InstallRuby, "kompo/tasks/install_ruby"
31
+ autoload :CheckStdlibs, "kompo/tasks/check_stdlibs"
28
32
 
29
33
  # Bundle and gem tasks
30
- autoload :BundleInstall, 'kompo/tasks/bundle_install'
31
- autoload :FindNativeExtensions, 'kompo/tasks/find_native_extensions'
32
- autoload :BuildNativeGem, 'kompo/tasks/build_native_gem'
34
+ autoload :BundleInstall, "kompo/tasks/bundle_install"
35
+ autoload :FindNativeExtensions, "kompo/tasks/find_native_extensions"
36
+ autoload :BuildNativeGem, "kompo/tasks/build_native_gem"
33
37
 
34
38
  # Final packing (Section: macOS uses clang, Linux uses gcc)
35
- autoload :CollectDependencies, 'kompo/tasks/collect_dependencies'
36
- autoload :Packing, 'kompo/tasks/packing'
39
+ autoload :CollectDependencies, "kompo/tasks/collect_dependencies"
40
+ autoload :Packing, "kompo/tasks/packing"
37
41
 
38
42
  # Homebrew path (macOS)
39
- autoload :HomebrewPath, 'kompo/tasks/homebrew'
43
+ autoload :HomebrewPath, "kompo/tasks/homebrew"
40
44
 
41
45
  # Platform-specific dependencies (Section: macOS uses Homebrew, Linux checks via pkg-config)
42
- autoload :InstallDeps, 'kompo/tasks/install_deps'
46
+ autoload :InstallDeps, "kompo/tasks/install_deps"
43
47
 
44
48
  # External tool paths
45
- autoload :KompoVfsPath, 'kompo/tasks/kompo_vfs_path'
46
- autoload :KompoVfsVersionCheck, 'kompo/tasks/kompo_vfs_version_check'
47
- autoload :RubyBuildPath, 'kompo/tasks/ruby_build_path'
48
- autoload :CargoPath, 'kompo/tasks/cargo_path'
49
+ autoload :KompoVfsPath, "kompo/tasks/kompo_vfs_path"
50
+ autoload :KompoVfsVersionCheck, "kompo/tasks/kompo_vfs_version_check"
51
+ autoload :RubyBuildPath, "kompo/tasks/ruby_build_path"
52
+ autoload :CargoPath, "kompo/tasks/cargo_path"
49
53
  end
50
54
 
51
55
  # Load cache methods (module methods, not autoloadable)
52
- require_relative 'kompo/cache'
56
+ require_relative "kompo/cache"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kompo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sho Hirano