kompo 0.1.4 → 0.3.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.
data/lib/kompo.rb CHANGED
@@ -1,356 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "tmpdir"
4
- require 'pathname'
5
- require 'optparse'
6
- require "fileutils"
7
- require 'forwardable'
8
- require_relative "kompo/version"
3
+ require 'taski'
4
+ require_relative 'kompo/version'
9
5
 
10
6
  module Kompo
11
- class Error < StandardError; end
12
-
13
- class Option
14
- extend Forwardable
15
- attr_accessor :entrypoint, :output, :gemfile, :stdlib, :dest_dir, :ruby_src_path, :cache_bundle_path, :ruby_version, :compress, :context, :args
16
- delegate %i[on] => :@opt
17
-
18
- def initialize(dir = Dir.getwd, opt = OptionParser.new)
19
- @entrypoint = File.join(dir, 'main.rb')
20
- @output = File.basename(dir)
21
- @gemfile = File.exist?(File.join(Dir.pwd, 'Gemfile'))
22
- @stdlib = true
23
- @dest_dir = dir
24
- @ruby_src_path = nil
25
- @cache_bundle_path = nil
26
- @ruby_version = "v#{RUBY_VERSION.gsub('.', '_')}"
27
- @compress = false
28
-
29
- @context = dir
30
- @opt = opt
31
- end
32
-
33
- def build
34
- @opt.parse!(ARGV)
35
-
36
- @args = convert_absolute_path_for ARGV
37
-
38
- self
39
- end
40
-
41
- private
42
-
43
- def convert_absolute_path_for(args)
44
- args.map do |arg|
45
- if File.absolute_path?(arg)
46
- arg
47
- else
48
- Pathname.new(File.join(context, arg)).cleanpath.to_s
49
- end
50
- end
51
- end
52
- end
53
-
54
- class Tasks
55
- extend Forwardable
56
- attr_reader :task, :fs, :work_dir, :ruby_src_dir, :ruby_pc, :ruby_bin, :extinit_o, :encinit_o, :lib_ruby_static_dir, :bundle_setup, :bundle_ruby, :std_libs, :gem_libs
57
-
58
- delegate %i[entrypoint output gemfile stdlib dest_dir ruby_src_path cache_bundle_path ruby_version compress context args] => :@option
59
- delegate %i[komop_cli lib_kompo_dir] => :@fs
60
-
61
- def initialize(option, dir)
62
- @option = option
63
- @ruby_src_dir = File.expand_path(ruby_src_path || File.join(dir, 'ruby'))
64
- @work_dir = dir
65
- @fs = Fs.new
66
-
67
- @ruby_pc = File.join(ruby_src_path || File.join(dir, 'dest_dir', 'lib', 'pkgconfig'), 'ruby.pc')
68
- @ruby_bin = File.join(ruby_src_path || File.join(dir, 'dest_dir', 'bin'), 'ruby')
69
- @extinit_o = File.join(ruby_src_dir, 'ext', 'extinit.o')
70
- @encinit_o = File.join(ruby_src_dir, 'enc', 'encinit.o')
71
- @lib_ruby_static_dir = ruby_src_path || File.join(dir, 'dest_dir', 'lib')
72
-
73
- @std_libs = []
74
- @gem_libs = []
75
- end
76
-
77
- def valid?
78
- raise "kompo-cli not found. Please install 'kompo-cli'." unless komop_cli
79
- raise "libkompo_fs.a not found. Please install 'kompo-cli'." unless lib_kompo_dir
80
- raise "Entrypoint not found: '#{entrypoint}'. Please specify the entry file path with '-e' or '--entrypoint' option." unless File.exist?(entrypoint)
81
-
82
- true
83
- end
84
-
85
- def self.cd_work_dir(option)
86
- Dir.mktmpdir do |dir|
87
- task = new(option.build, dir)
88
- task.valid?
89
- FileUtils.cd(dir)
90
-
91
- yield task
92
- end
93
- end
94
-
95
- def clone_ruby_src
96
- if ruby_src_path.nil?
97
- command = ['git', '-C', "#{work_dir}", 'clone', '-b', "#{ruby_version}", '--single-branch', '--depth=1', 'https://github.com/ruby/ruby.git'].join(' ')
98
- exec_command command, 'git clone ruby.git'
99
-
100
- Dir.chdir(ruby_src_dir) do
101
- exec_command (File.exist?("#{ruby_src_dir}/autogen.sh") ? './autogen.sh' : "autoconf"), 'autoxxx'
102
-
103
- command = [
104
- './configure',
105
- "--prefix=#{work_dir}/dest_dir",
106
- "--disable-install-doc",
107
- "--disable-install-rdoc",
108
- "--disable-install-capi",
109
- "--with-static-linked-ext",
110
- "--with-ruby-pc=ruby.pc",
111
- "--with-ext=#{get_exts_dir}"
112
- ].join(' ')
113
- exec_command command, 'configure'
114
-
115
- exec_command ['make', 'install'].join(' '), 'build target version ruby'
116
- end
117
- end
118
- end
119
-
120
- def get_from_ruby_pc(option)
121
- command = [
122
- 'pkg-config',
123
- "#{option}",
124
- "#{ruby_pc}"
125
- ].join(' ')
126
-
127
- exec_command(command, 'pkg-config', true)
128
- end
129
-
130
- def bundle_install
131
- if cache_bundle_path
132
- FileUtils.cp_r(cache_bundle_path, work_dir)
133
- @bundle_setup = File.join(cache_bundle_path, 'bundler', 'setup.rb')
134
- @bundle_ruby = File.join(cache_bundle_path, 'ruby')
135
- else
136
- File.write('./bundler', File.read(`which bundle`.chomp).split("\n").tap { _1[0] = "#!#{ruby_bin}" }.join("\n"))
137
- FileUtils.chmod(0755, './bundler')
138
-
139
- command = [
140
- './bundler',
141
- 'install',
142
- '--standalone'
143
- ].join(' ')
144
-
145
- exec_command command, 'bundle install'
146
-
147
- @bundle_setup = File.join(work_dir, 'bundle', 'bundler', 'setup.rb')
148
- @bundle_ruby = File.join(work_dir, 'bundle', 'ruby')
149
- end
150
- end
151
-
152
- def fs_cli
153
- command = [
154
- komop_cli,
155
- context,
156
- args.join(' '),
157
- get_load_paths,
158
- "--entrypoint=#{entrypoint}",
159
- ].join(' ')
160
-
161
- exec_command command, 'kompo-cli'
162
- end
163
-
164
- def make_main_c
165
- require 'erb'
166
-
167
- exts = []
168
- if gemfile
169
- Dir.glob(File.join(bundle_ruby, get_semantic_ruby_version, 'gems/**/extconf.rb')).each do |makefile_dir|
170
- dir_name = File.dirname(makefile_dir)
171
- makefile = File.join(dir_name, 'Makefile')
172
- if File.exist?(cargo_toml = File.join(dir_name, 'Cargo.toml'))
173
- command = [
174
- 'cargo',
175
- 'rustc',
176
- '--release',
177
- '--crate-type=staticlib',
178
- '--target-dir',
179
- 'target',
180
- "--manifest-path=#{cargo_toml}",
181
- ].join(' ')
182
- exec_command command, 'cargo build'
183
- copy_targets = Dir.glob(File.join(dir_name, 'target/release/*.a'))
184
- else
185
- objs = File.read(makefile).scan(/OBJS = (.*\.o)/).join(' ')
186
- command = ['make', '-C', dir_name, objs, '--always-make'].join(' ')
187
- exec_command command, 'make'
188
- copy_targets = objs.split(' ').map { File.join(dir_name, _1) }
189
- end
190
-
191
- dir = FileUtils.mkdir_p('exts/' + File.basename(dir_name)).first
192
- FileUtils.cp(copy_targets, dir)
193
- prefix = File.read(makefile).scan(/target_prefix = (.*)/).join.delete_prefix('/')
194
- target_name = File.read(makefile).scan(/TARGET_NAME = (.*)/).join
195
- exts << [File.join(prefix, "#{target_name}.so").delete_prefix('/'), "Init_#{target_name}"]
196
- end
197
- end
198
-
199
- File.write("main.c", ERB.new(File.read(File.join(__dir__, 'main.c.erb'))).result(binding))
200
- end
201
-
202
- def packing
203
- command = [
204
- 'gcc',
205
- '-O3',
206
- '-Wall',
207
- 'main.c',
208
- Dir.glob('exts/**/*.o').join(' '),
209
- 'fs.o',
210
- "#{lib_ruby_static_dir.nil? ? '' : '-L' + lib_ruby_static_dir}",
211
- "#{lib_kompo_dir.nil? ? '' : '-L' + lib_kompo_dir}",
212
- get_ruby_header,
213
- get_exts,
214
- '-lkompo',
215
- '-lruby-static',
216
- get_libs,
217
- '-o',
218
- output
219
- ].join(' ')
220
-
221
- exec_command command, 'Packing'
222
- end
223
-
224
- def copy_to_dest_dir
225
- command = ['cp', '-f', output, dest_dir].join(' ')
226
- exec_command command, 'Copy to dest dir'
227
- end
228
-
229
- private
230
-
231
- def exec_command(command, info = nil, ret = false)
232
- puts "exec: #{info}" if info
233
- puts command
234
- if ret
235
- ret = `#{command}`.chomp
236
- if $?.exited?
237
- ret
238
- else
239
- raise "Failed to execute command: #{command}"
240
- end
241
- else
242
- system command, exception: true
243
- end
244
- end
245
-
246
- def get_exts
247
- ["#{extinit_o}", "#{encinit_o}", *Dir.glob("#{ruby_src_dir}/ext/**/*.a"), *Dir.glob("#{ruby_src_dir}/enc/**/*.a")].join(' ')
248
- end
249
-
250
- def get_libs
251
- main_lib = get_mainlibs
252
- ext_libs = Dir.glob("#{ruby_src_dir}/ext/**/exts.mk").flat_map { File.read(_1).scan(/EXTLIBS = (.*)/) }.join(" ")
253
- gem_libs = Dir.glob("bundle/ruby/#{get_semantic_ruby_version}/gems/*/ext/*/Makefile").flat_map{ File.read(_1).scan(/LIBS = (.*)/)}.join(" ")
254
- dyn, static = eval("%W[#{main_lib} #{ext_libs} #{gem_libs}]").uniq
255
- .partition { _1 == "-lpthread" || _1 == "-ldl" || _1 == "-lm" || _1 == "-lc" }
256
- dyn.unshift "-Wl,-Bdynamic"
257
- static.unshift "-Wl,-Bstatic"
258
-
259
- static.join(" ") + " " + dyn.join(" ")
260
- end
261
-
262
- def get_ruby_header
263
- get_from_ruby_pc('--cflags')
264
- end
265
-
266
- def get_semantic_ruby_version
267
- get_from_ruby_pc('--variable=ruby_version')
268
- end
269
-
270
- def get_mainlibs
271
- get_from_ruby_pc('--variable=MAINLIBS')
272
- end
273
-
274
- def get_load_paths
275
- load_paths = []
276
- if gemfile
277
- load_paths += gem_libs
278
- end
279
-
280
- if stdlib
281
- load_paths += std_libs
282
- end
283
-
284
- load_paths
285
- end
286
-
287
- def get_exts_dir
288
- Dir.glob("#{ruby_src_dir}/**/extconf.rb")
289
- .reject { _1 =~ /-test-/ }
290
- .reject { _1 =~ /win32/ } # TODO
291
- .map { File.dirname(_1) }
292
- .map { _1.split("#{ruby_src_dir}/ext/")[1] }
293
- .join(',')
294
- end
295
-
296
- def std_libs
297
- return [] unless stdlib
298
- return @std_libs unless @std_libs.empty?
299
-
300
- command = ["#{ruby_bin}", '-e', "'puts $:'"].join(' ')
301
-
302
- @std_libs = exec_command(command, 'Check std_libs', true).split("\n")
303
- end
304
-
305
- def gem_libs
306
- return [] unless gemfile
307
- return @gem_libs unless @gem_libs.empty?
308
-
309
- FileUtils.cp_r(File.join(context, 'Gemfile'), work_dir)
310
- FileUtils.cp_r(File.join(context, 'Gemfile.lock'), work_dir)
311
-
312
- bundle_install
313
-
314
- command = [
315
- "#{ruby_bin}",
316
- '-r',
317
- "#{bundle_setup}",
318
- '-e',
319
- "'puts $:'"
320
- ].join(' ')
321
-
322
- @gem_libs = (exec_command(command, 'Check gem_libs', true).split("\n") - std_libs)
323
- end
324
- end
325
-
326
- class Fs
327
- attr_reader :komop_cli, :lib_kompo_dir
328
-
329
- def initialize
330
- @komop_cli = local_komop_cli || ENV['KOMPO_CLI']
331
- @lib_kompo_dir = local_lib_kompo_dir || ENV['LIB_KOMPO_DIR']
332
- end
333
-
334
- def local_komop_cli
335
- return nil if `which brew`.empty?
336
-
337
- path = `brew --prefix kompo-vfs`.chomp + '/bin/kompo-cli'
338
- if File.exist?(path)
339
- path
340
- else
341
- nil
342
- end
343
- end
344
-
345
- def local_lib_kompo_dir
346
- return nil if `which brew`.empty?
347
-
348
- path = `brew --prefix kompo-vfs`.chomp + '/lib'
349
- if File.exist?(path)
350
- path
351
- else
352
- nil
353
- end
354
- end
355
- end
7
+ # Fixed path prefix for Ruby installation
8
+ # Using a fixed path ensures that cached Ruby binaries work correctly,
9
+ # since Ruby has hardcoded paths for standard library locations.
10
+ KOMPO_PREFIX = '/kompo'
11
+
12
+ # Utility classes
13
+ autoload :KompoIgnore, 'kompo/kompo_ignore'
14
+
15
+ # Struct to hold file data for embedding (used by MakeFsC)
16
+ autoload :KompoFile, 'kompo/tasks/make_fs_c'
17
+
18
+ # 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'
24
+
25
+ # Ruby installation
26
+ autoload :InstallRuby, 'kompo/tasks/install_ruby'
27
+ autoload :CheckStdlibs, 'kompo/tasks/check_stdlibs'
28
+
29
+ # 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'
33
+
34
+ # Final packing (Section: macOS uses clang, Linux uses gcc)
35
+ autoload :CollectDependencies, 'kompo/tasks/collect_dependencies'
36
+ autoload :Packing, 'kompo/tasks/packing'
37
+
38
+ # Homebrew path (macOS)
39
+ autoload :HomebrewPath, 'kompo/tasks/homebrew'
40
+
41
+ # Platform-specific dependencies (Section: macOS uses Homebrew, Linux checks via pkg-config)
42
+ autoload :InstallDeps, 'kompo/tasks/install_deps'
43
+
44
+ # 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'
356
49
  end
50
+
51
+ # Load cache methods (module methods, not autoloadable)
52
+ require_relative 'kompo/cache'
data/lib/main.c.erb CHANGED
@@ -1,39 +1,58 @@
1
1
  #include <ruby.h>
2
- #include <string.h>
3
2
 
4
- extern char *get_kompo_patch(void);
5
3
  extern void ruby_init_ext(const char *name, void (*init)(void));
6
4
  extern void Init_kompo_fs(void);
7
- extern char *get_start_file_name(void);
8
- <% exts.each do |(_, func)| %>
5
+ extern void kompo_fs_set_entrypoint_dir(const char *entrypoint_path);
6
+ <% context.exts.each do |(_, func)| %>
9
7
  extern void <%= func %>(void);
10
8
  <% end %>
11
9
  void Init_gems(void)
12
10
  {
13
- <% exts.each do |(so_path, func)| %>
14
- ruby_init_ext("<%= so_path %>", <%= func %>);
11
+ <% context.exts.each do |(ext_path, func)| %>
12
+ ruby_init_ext("<%= ext_path %>.so", <%= func %>);
15
13
  <% end %>
16
14
  }
17
15
 
18
16
  int main(int argc, char **argv)
19
17
  {
20
- int c = 3;
21
-
22
- // HACK: argv[0] is rewritten and reused by setproctitle()
23
- argv[1] = "-e";
24
- argv[2] = get_kompo_patch();
18
+ <% if context.has_gemfile %>
19
+ int c = argc + 3;
20
+ char *argv2[c];
21
+
22
+ argv2[0] = argv[0];
23
+ argv2[1] = (char *)"-r";
24
+ argv2[2] = (char *)"bundler/setup";
25
+ argv2[3] = (char *)"<%= context.work_dir_entrypoint %>";
26
+ for (int i = 1; i < argc; i++) {
27
+ argv2[i + 3] = argv[i];
28
+ }
29
+ <% else %>
30
+ int c = argc + 1;
31
+ char *argv2[c];
32
+
33
+ argv2[0] = argv[0];
34
+ argv2[1] = (char *)"<%= context.work_dir_entrypoint %>";
35
+ for (int i = 1; i < argc; i++) {
36
+ argv2[i + 1] = argv[i];
37
+ }
38
+ <% end %>
25
39
 
26
- ruby_sysinit(&c, &argv);
40
+ char **argv2_ptr = argv2;
41
+ ruby_sysinit(&c, &argv2_ptr);
27
42
 
28
43
  RUBY_INIT_STACK;
29
44
  ruby_init();
30
45
 
31
46
  Init_kompo_fs();
47
+
48
+ const char *entrypoint = "<%= context.work_dir_entrypoint %>";
49
+ kompo_fs_set_entrypoint_dir(entrypoint);
50
+
32
51
  Init_gems();
33
52
 
34
- void *node = ruby_options(c, argv);
53
+ void *node = ruby_options(c, argv2_ptr);
35
54
 
36
55
  // set $0
37
- ruby_script(get_start_file_name());
56
+ ruby_script("<%= File.basename(context.project_dir) %>");
38
57
  return ruby_run_node(node);
39
58
  }
@@ -0,0 +1,116 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: ast
5
+ version: '2.4'
6
+ source:
7
+ type: git
8
+ name: ruby/gem_rbs_collection
9
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
10
+ remote: https://github.com/ruby/gem_rbs_collection.git
11
+ repo_dir: gems
12
+ - name: async
13
+ version: '2.12'
14
+ source:
15
+ type: git
16
+ name: ruby/gem_rbs_collection
17
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
18
+ remote: https://github.com/ruby/gem_rbs_collection.git
19
+ repo_dir: gems
20
+ - name: fileutils
21
+ version: '0'
22
+ source:
23
+ type: stdlib
24
+ - name: json
25
+ version: '0'
26
+ source:
27
+ type: stdlib
28
+ - name: logger
29
+ version: '0'
30
+ source:
31
+ type: stdlib
32
+ - name: monitor
33
+ version: '0'
34
+ source:
35
+ type: stdlib
36
+ - name: optparse
37
+ version: '0'
38
+ source:
39
+ type: stdlib
40
+ - name: parallel
41
+ version: '1.20'
42
+ source:
43
+ type: git
44
+ name: ruby/gem_rbs_collection
45
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
46
+ remote: https://github.com/ruby/gem_rbs_collection.git
47
+ repo_dir: gems
48
+ - name: parser
49
+ version: '3.2'
50
+ source:
51
+ type: git
52
+ name: ruby/gem_rbs_collection
53
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
54
+ remote: https://github.com/ruby/gem_rbs_collection.git
55
+ repo_dir: gems
56
+ - name: pathname
57
+ version: '0'
58
+ source:
59
+ type: stdlib
60
+ - name: prism
61
+ version: 1.4.0
62
+ source:
63
+ type: rubygems
64
+ - name: rainbow
65
+ version: '3.0'
66
+ source:
67
+ type: git
68
+ name: ruby/gem_rbs_collection
69
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
70
+ remote: https://github.com/ruby/gem_rbs_collection.git
71
+ repo_dir: gems
72
+ - name: rake
73
+ version: '13.0'
74
+ source:
75
+ type: git
76
+ name: ruby/gem_rbs_collection
77
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
78
+ remote: https://github.com/ruby/gem_rbs_collection.git
79
+ repo_dir: gems
80
+ - name: rbs
81
+ version: 3.9.2
82
+ source:
83
+ type: rubygems
84
+ - name: rdoc
85
+ version: '0'
86
+ source:
87
+ type: stdlib
88
+ - name: regexp_parser
89
+ version: '2.8'
90
+ source:
91
+ type: git
92
+ name: ruby/gem_rbs_collection
93
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
94
+ remote: https://github.com/ruby/gem_rbs_collection.git
95
+ repo_dir: gems
96
+ - name: rubocop
97
+ version: '1.57'
98
+ source:
99
+ type: git
100
+ name: ruby/gem_rbs_collection
101
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
102
+ remote: https://github.com/ruby/gem_rbs_collection.git
103
+ repo_dir: gems
104
+ - name: rubocop-ast
105
+ version: '1.30'
106
+ source:
107
+ type: git
108
+ name: ruby/gem_rbs_collection
109
+ revision: 2813b6be44d7617d5defa2aaecdbd4540ee1c418
110
+ remote: https://github.com/ruby/gem_rbs_collection.git
111
+ repo_dir: gems
112
+ - name: tsort
113
+ version: '0'
114
+ source:
115
+ type: stdlib
116
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,19 @@
1
+ # Download sources
2
+ sources:
3
+ - type: git
4
+ name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
8
+
9
+ # You can specify local directories as sources also.
10
+ # - type: local
11
+ # path: path/to/your/local/repository
12
+
13
+ # A directory to install the downloaded RBSs
14
+ path: .gem_rbs_collection
15
+
16
+ # gems:
17
+ # # If you want to avoid installing rbs files for gems, you can specify them here.
18
+ # - name: GEM_NAME
19
+ # ignore: true