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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/Gemfile +7 -7
- data/Gemfile.lock +2 -2
- data/README.md +38 -13
- data/Rakefile +6 -6
- data/exe/kompo +21 -19
- data/lib/kompo/cache.rb +7 -7
- data/lib/kompo/kompo_ignore.rb +2 -2
- data/lib/kompo/tasks/build_native_gem.rb +48 -15
- data/lib/kompo/tasks/bundle_install.rb +42 -42
- data/lib/kompo/tasks/cargo_path.rb +13 -13
- data/lib/kompo/tasks/check_stdlibs.rb +6 -6
- data/lib/kompo/tasks/collect_dependencies.rb +23 -15
- data/lib/kompo/tasks/copy_gemfile.rb +19 -11
- data/lib/kompo/tasks/copy_project_files.rb +4 -4
- data/lib/kompo/tasks/find_native_extensions.rb +80 -17
- data/lib/kompo/tasks/homebrew.rb +15 -15
- data/lib/kompo/tasks/install_deps.rb +36 -36
- data/lib/kompo/tasks/kompo_vfs_path.rb +41 -32
- data/lib/kompo/tasks/kompo_vfs_version_check.rb +6 -4
- data/lib/kompo/tasks/make_fs_c.rb +30 -24
- data/lib/kompo/tasks/make_main_c.rb +6 -6
- data/lib/kompo/tasks/packing.rb +63 -48
- data/lib/kompo/tasks/ruby_build_path.rb +60 -15
- data/lib/kompo/version.rb +2 -2
- data/lib/kompo.rb +28 -24
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77c057a8598fe8c02319c0635d2bf80cca3578f2606f676adae992dcf34afa68
|
|
4
|
+
data.tar.gz: 68dfc19d5b9484b5fabe79cd18f8f8bb11368fb0f788a7adc38a44d94aa7aa21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1da9b9c3b8f2903977b3ab503822a52586e3cded7af5c96651a3e423efa3e324e99fd82462fcbeba23a9bda06b80a90fe66239be62230b5f39cb6001387ee09
|
|
7
|
+
data.tar.gz: 63f90fcac1d59773595e696378573e8a1bfbb1cea9581145b729bc7e842dc479a982f0589acdf2f1823b8a21448df4fd8a3c434868e0ba8c281d47d13daed164
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2025-01-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `--dry-run` option to show compile command without executing [#17](https://github.com/ahogappa/kompo/pull/17)
|
|
14
|
+
- `--no-gemfile` option to skip Gemfile processing [#13](https://github.com/ahogappa/kompo/pull/13)
|
|
15
|
+
- `.kompoignore` files support for ignoring specific files in VFS [#20](https://github.com/ahogappa/kompo/pull/20)
|
|
16
|
+
- Duplicate file detection in VFS embedding [#15](https://github.com/ahogappa/kompo/pull/15)
|
|
17
|
+
- standardrb lint check to CI [#14](https://github.com/ahogappa/kompo/pull/14)
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Set default progress mode to simple [#16](https://github.com/ahogappa/kompo/pull/16)
|
|
21
|
+
- Bump kompo-vfs minimum version to 0.5.1 [#23](https://github.com/ahogappa/kompo/pull/23)
|
|
22
|
+
- Use Taski.message instead of puts for dry-run output [#21](https://github.com/ahogappa/kompo/pull/21)
|
|
23
|
+
- Remove redundant compile command label from dry-run output [#22](https://github.com/ahogappa/kompo/pull/22)
|
|
24
|
+
- Update README to match actual implementation
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- Extract LIBS from Makefiles including bundled gems [#19](https://github.com/ahogappa/kompo/pull/19)
|
|
28
|
+
- Support bundled gems native extensions for Ruby 4.0+ [#12](https://github.com/ahogappa/kompo/pull/12)
|
|
29
|
+
|
|
10
30
|
## [0.3.2] - 2025-01-25
|
|
11
31
|
|
|
12
32
|
### Fixed
|
data/Gemfile
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
source
|
|
3
|
+
source "https://rubygems.org"
|
|
4
4
|
|
|
5
5
|
# Specify your gem's dependencies in kompo.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
-
gem
|
|
9
|
-
gem
|
|
8
|
+
gem "rake", "~> 13.0"
|
|
9
|
+
gem "taski", "~> 0.8.0"
|
|
10
10
|
|
|
11
11
|
group :development, :test do
|
|
12
|
-
gem
|
|
13
|
-
gem
|
|
14
|
-
gem
|
|
15
|
-
gem
|
|
12
|
+
gem "debug"
|
|
13
|
+
gem "minitest"
|
|
14
|
+
gem "simplecov", require: false
|
|
15
|
+
gem "standard"
|
|
16
16
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
kompo (0.
|
|
4
|
+
kompo (0.4.0)
|
|
5
5
|
async
|
|
6
6
|
mini_portile2
|
|
7
7
|
pathspec
|
|
@@ -103,7 +103,7 @@ GEM
|
|
|
103
103
|
lint_roller (~> 1.1)
|
|
104
104
|
rubocop-performance (~> 1.26.0)
|
|
105
105
|
stringio (3.2.0)
|
|
106
|
-
taski (0.8.
|
|
106
|
+
taski (0.8.3)
|
|
107
107
|
prism (~> 1.4)
|
|
108
108
|
tsort
|
|
109
109
|
traces (0.18.2)
|
data/README.md
CHANGED
|
@@ -33,21 +33,36 @@ $ gem install kompo
|
|
|
33
33
|
|
|
34
34
|
## Usage
|
|
35
35
|
|
|
36
|
-
###
|
|
37
|
-
|
|
36
|
+
### Prerequisites
|
|
37
|
+
|
|
38
|
+
Kompo requires [kompo-vfs](https://github.com/ahogappa/kompo-vfs).
|
|
39
|
+
|
|
40
|
+
#### macOS (Homebrew required)
|
|
41
|
+
|
|
42
|
+
On macOS, [Homebrew](https://brew.sh) is required to install kompo-vfs:
|
|
38
43
|
|
|
39
|
-
#### Homebrew
|
|
40
44
|
```sh
|
|
41
45
|
$ brew tap ahogappa/kompo-vfs https://github.com/ahogappa/kompo-vfs.git
|
|
42
46
|
$ brew install ahogappa/kompo-vfs/kompo-vfs
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
#### Linux
|
|
50
|
+
|
|
51
|
+
On Linux, kompo-vfs is automatically built from source. You need to have Cargo (Rust) installed:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### For Development
|
|
58
|
+
|
|
59
|
+
If you want to use a local build of kompo-vfs, build it manually and use the `--local-vfs-path` option:
|
|
60
|
+
|
|
47
61
|
```sh
|
|
48
62
|
$ git clone https://github.com/ahogappa/kompo-vfs.git
|
|
49
63
|
$ cd kompo-vfs
|
|
50
64
|
$ cargo build --release
|
|
65
|
+
$ kompo --local-vfs-path=/path/to/kompo-vfs ...
|
|
51
66
|
```
|
|
52
67
|
|
|
53
68
|
## Options
|
|
@@ -62,10 +77,13 @@ Options:
|
|
|
62
77
|
--ruby-source=PATH Path to Ruby source tarball or directory
|
|
63
78
|
--no-cache Build Ruby from source, ignoring cache
|
|
64
79
|
--no-stdlib Exclude Ruby standard library from binary
|
|
80
|
+
--no-gemfile Skip Gemfile processing (no bundle install)
|
|
65
81
|
--local-vfs-path=PATH Path to local kompo-vfs for development
|
|
66
82
|
--clean[=VERSION] Clean cache (current version by default, or specify VERSION, or "all")
|
|
83
|
+
--dry-run Show final compile command without executing it
|
|
67
84
|
-t, --tree Show task dependency tree and exit
|
|
68
|
-
-
|
|
85
|
+
-v, --version Show version
|
|
86
|
+
-h, --help Show this help message
|
|
69
87
|
|
|
70
88
|
Files:
|
|
71
89
|
Additional files and directories to include in the binary
|
|
@@ -81,9 +99,12 @@ Files:
|
|
|
81
99
|
| `--ruby-source` | Use a local Ruby source instead of downloading. Useful for custom Ruby builds. |
|
|
82
100
|
| `--no-cache` | Force a fresh Ruby build, ignoring any cached version. |
|
|
83
101
|
| `--no-stdlib` | Reduce binary size by excluding Ruby standard library. Only use if your app doesn't need stdlib. |
|
|
102
|
+
| `--no-gemfile` | Skip Gemfile processing and bundle install. Useful when your project doesn't use Bundler. |
|
|
84
103
|
| `--local-vfs-path` | Use a local kompo-vfs build instead of Homebrew installation. Useful for development. |
|
|
85
104
|
| `--clean` | Remove cached Ruby builds. Use `--clean=all` to remove all versions. |
|
|
105
|
+
| `--dry-run` | Show the final compile command without executing it. Useful for debugging build issues. |
|
|
86
106
|
| `-t, --tree` | Display the task dependency graph and exit without building. |
|
|
107
|
+
| `-v, --version` | Display the kompo version and exit. |
|
|
87
108
|
|
|
88
109
|
### Examples
|
|
89
110
|
|
|
@@ -137,14 +158,18 @@ node_modules/ # Ignore node_modules
|
|
|
137
158
|
- Patterns are matched against paths relative to the project root
|
|
138
159
|
- Comments and empty lines are ignored
|
|
139
160
|
|
|
140
|
-
##
|
|
161
|
+
## Samples
|
|
162
|
+
|
|
163
|
+
Sample applications demonstrating various use cases are available in the [samples](./samples) directory:
|
|
141
164
|
|
|
142
|
-
* hello
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
165
|
+
* [hello](./samples/hello)
|
|
166
|
+
* Simple hello world script.
|
|
167
|
+
* [native_gems](./samples/native_gems)
|
|
168
|
+
* Demonstrates native extension gems (nokogiri, sqlite3, and msgpack).
|
|
169
|
+
* [sinatra_and_sqlite](./samples/sinatra_and_sqlite)
|
|
170
|
+
* Simple Sinatra app with SQLite3.
|
|
171
|
+
* [rails](./samples/rails/sample)
|
|
172
|
+
* Simple Rails application.
|
|
148
173
|
|
|
149
174
|
## Development
|
|
150
175
|
|
data/Rakefile
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/testtask"
|
|
5
5
|
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
|
7
|
-
t.libs <<
|
|
8
|
-
t.libs <<
|
|
9
|
-
t.test_files = FileList[
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
require
|
|
12
|
+
require "standard/rake"
|
|
13
13
|
|
|
14
14
|
task default: %i[test standard]
|
data/exe/kompo
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require
|
|
5
|
-
require_relative
|
|
4
|
+
require "optparse"
|
|
5
|
+
require_relative "../lib/kompo"
|
|
6
6
|
|
|
7
7
|
args = {}
|
|
8
8
|
opt = OptionParser.new do |o|
|
|
9
|
-
o.banner =
|
|
10
|
-
o.separator
|
|
11
|
-
o.separator
|
|
9
|
+
o.banner = "Usage: kompo [options] [files...]"
|
|
10
|
+
o.separator ""
|
|
11
|
+
o.separator "Options:"
|
|
12
12
|
|
|
13
|
-
o.on(
|
|
14
|
-
o.on(
|
|
15
|
-
o.on(
|
|
16
|
-
o.on(
|
|
13
|
+
o.on("-e", "--entrypoint=FILE", "Entry point file (default: main.rb)") { |v| args[:entrypoint] = v }
|
|
14
|
+
o.on("-o", "--output=DIR", "Output directory for the binary") { |v| args[:output_dir] = File.expand_path(v) }
|
|
15
|
+
o.on("--ruby-version=VERSION", "Ruby version to use (default: #{RUBY_VERSION})") { |v| args[:ruby_version] = v }
|
|
16
|
+
o.on("--ruby-source=PATH", "Path to Ruby source tarball or directory") do |v|
|
|
17
17
|
args[:ruby_source_path] = File.expand_path(v)
|
|
18
18
|
end
|
|
19
|
-
o.on(
|
|
20
|
-
o.on(
|
|
21
|
-
o.on(
|
|
19
|
+
o.on("--no-cache", "Build Ruby from source, ignoring cache") { |_v| args[:no_cache] = true }
|
|
20
|
+
o.on("--no-stdlib", "Exclude Ruby standard library from binary") { |_v| args[:no_stdlib] = true }
|
|
21
|
+
o.on("--no-gemfile", "Skip Gemfile processing (no bundle install)") { |_v| args[:no_gemfile] = true }
|
|
22
|
+
o.on("--local-vfs-path=PATH", "Path to local kompo-vfs for development") do |v|
|
|
22
23
|
args[:local_kompo_vfs_path] = File.expand_path(v)
|
|
23
24
|
end
|
|
24
|
-
o.on(
|
|
25
|
+
o.on("--clean[=VERSION]", 'Clean cache (current Ruby version by default, or specify VERSION, or "all")') do |v|
|
|
25
26
|
args[:clean_cache] = v.nil? ? RUBY_VERSION : v
|
|
26
27
|
end
|
|
27
|
-
o.on(
|
|
28
|
+
o.on("--dry-run", "Show final compile command without executing it") { |_v| args[:dry_run] = true }
|
|
29
|
+
o.on("-t", "--tree", "Show task dependency tree and exit") do
|
|
28
30
|
puts Kompo::Packing.tree
|
|
29
31
|
exit(0)
|
|
30
32
|
end
|
|
31
|
-
o.on(
|
|
33
|
+
o.on("-v", "--version", "Show version") do
|
|
32
34
|
puts "kompo #{Kompo::VERSION}"
|
|
33
35
|
exit(0)
|
|
34
36
|
end
|
|
35
|
-
o.on(
|
|
37
|
+
o.on("-h", "--help", "Show this help message") do
|
|
36
38
|
puts o
|
|
37
39
|
exit(0)
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
o.separator
|
|
41
|
-
o.separator
|
|
42
|
-
o.separator
|
|
42
|
+
o.separator ""
|
|
43
|
+
o.separator "Files:"
|
|
44
|
+
o.separator " Additional files and directories to include in the binary"
|
|
43
45
|
end
|
|
44
46
|
opt.parse!(ARGV)
|
|
45
47
|
args[:files] = ARGV
|
data/lib/kompo/cache.rb
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "fileutils"
|
|
4
4
|
|
|
5
5
|
module Kompo
|
|
6
6
|
# Clean the cache for specified Ruby version
|
|
7
7
|
# @param version [String] Ruby version to clean, or "all" to clean all caches
|
|
8
8
|
def self.clean_cache(version)
|
|
9
|
-
kompo_cache = File.expand_path(
|
|
9
|
+
kompo_cache = File.expand_path("~/.kompo/cache")
|
|
10
10
|
|
|
11
11
|
unless Dir.exist?(kompo_cache)
|
|
12
12
|
puts "Cache directory does not exist: #{kompo_cache}"
|
|
13
13
|
return
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
if version ==
|
|
16
|
+
if version == "all"
|
|
17
17
|
clean_all_caches(kompo_cache)
|
|
18
18
|
else
|
|
19
19
|
clean_version_cache(kompo_cache, version)
|
|
@@ -22,7 +22,7 @@ module Kompo
|
|
|
22
22
|
|
|
23
23
|
# Clean all caches in the cache directory
|
|
24
24
|
def self.clean_all_caches(kompo_cache)
|
|
25
|
-
entries = Dir.glob(File.join(kompo_cache,
|
|
25
|
+
entries = Dir.glob(File.join(kompo_cache, "*"))
|
|
26
26
|
if entries.empty?
|
|
27
27
|
puts "No caches found in #{kompo_cache}"
|
|
28
28
|
return
|
|
@@ -33,7 +33,7 @@ module Kompo
|
|
|
33
33
|
puts "Removed: #{entry}"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
puts
|
|
36
|
+
puts "All caches cleaned successfully"
|
|
37
37
|
end
|
|
38
38
|
private_class_method :clean_all_caches
|
|
39
39
|
|
|
@@ -52,8 +52,8 @@ module Kompo
|
|
|
52
52
|
real_version_cache = File.realpath(version_cache_dir)
|
|
53
53
|
|
|
54
54
|
unless real_version_cache.start_with?(real_kompo_cache + File::SEPARATOR) ||
|
|
55
|
-
|
|
56
|
-
puts
|
|
55
|
+
real_version_cache == real_kompo_cache
|
|
56
|
+
puts "Error: Invalid cache path detected (possible path traversal)"
|
|
57
57
|
return
|
|
58
58
|
end
|
|
59
59
|
|
data/lib/kompo/kompo_ignore.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "pathspec"
|
|
4
4
|
|
|
5
5
|
module Kompo
|
|
6
6
|
# Handler for .kompoignore file
|
|
7
7
|
# Uses pathspec gem for gitignore-compatible pattern matching
|
|
8
8
|
class KompoIgnore
|
|
9
|
-
FILENAME =
|
|
9
|
+
FILENAME = ".kompoignore"
|
|
10
10
|
|
|
11
11
|
def initialize(project_dir)
|
|
12
12
|
@project_dir = project_dir
|
|
@@ -43,18 +43,40 @@ module Kompo
|
|
|
43
43
|
def build_extension(ext, work_dir)
|
|
44
44
|
dir_name = ext[:dir_name]
|
|
45
45
|
gem_ext_name = ext[:gem_ext_name]
|
|
46
|
-
ext_type = ext[:is_rust] ? "Rust" : "C"
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
if ext[:is_prebuilt]
|
|
48
|
+
# Pre-built bundled gems only need registration for ruby_init_ext()
|
|
49
|
+
group("Registering #{gem_ext_name} (pre-built bundled gem)") do
|
|
50
|
+
register_prebuilt_extension(dir_name, gem_ext_name)
|
|
51
|
+
puts "Registered: #{gem_ext_name}"
|
|
53
52
|
end
|
|
53
|
+
else
|
|
54
|
+
ext_type = ext[:is_rust] ? "Rust" : "C"
|
|
55
|
+
group("Building #{gem_ext_name} (#{ext_type})") do
|
|
56
|
+
if ext[:is_rust]
|
|
57
|
+
build_rust_extension(dir_name, ext[:cargo_toml], gem_ext_name, work_dir)
|
|
58
|
+
else
|
|
59
|
+
build_c_extension(dir_name, gem_ext_name, work_dir)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
register_extension(dir_name, gem_ext_name)
|
|
63
|
+
puts "Built: #{gem_ext_name}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Register pre-built bundled gem extension for ruby_init_ext()
|
|
69
|
+
# These extensions are already compiled during Ruby build
|
|
70
|
+
def register_prebuilt_extension(dir_name, gem_ext_name)
|
|
71
|
+
makefile_path = File.join(dir_name, "Makefile")
|
|
54
72
|
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
unless File.exist?(makefile_path)
|
|
74
|
+
raise "Cannot register pre-built extension #{gem_ext_name}: Makefile not found in #{dir_name}"
|
|
57
75
|
end
|
|
76
|
+
|
|
77
|
+
makefile_content = File.read(makefile_path)
|
|
78
|
+
prefix, target_name = parse_makefile_metadata(makefile_content, gem_ext_name)
|
|
79
|
+
add_extension_entry(prefix, target_name)
|
|
58
80
|
end
|
|
59
81
|
|
|
60
82
|
def register_extension(dir_name, gem_ext_name)
|
|
@@ -63,8 +85,7 @@ module Kompo
|
|
|
63
85
|
if File.exist?(makefile_path)
|
|
64
86
|
# C extension: parse Makefile
|
|
65
87
|
makefile_content = File.read(makefile_path)
|
|
66
|
-
prefix = makefile_content
|
|
67
|
-
target_name = makefile_content.scan(/TARGET_NAME = (.*)/).flatten.first || gem_ext_name
|
|
88
|
+
prefix, target_name = parse_makefile_metadata(makefile_content, gem_ext_name)
|
|
68
89
|
else
|
|
69
90
|
# Rust extension: parse Cargo.toml
|
|
70
91
|
cargo_toml_path = File.join(dir_name, "Cargo.toml")
|
|
@@ -82,9 +103,7 @@ module Kompo
|
|
|
82
103
|
end
|
|
83
104
|
end
|
|
84
105
|
|
|
85
|
-
|
|
86
|
-
ext_path = File.join(prefix, target_name).delete_prefix("/")
|
|
87
|
-
@exts << [ext_path, "Init_#{target_name}"]
|
|
106
|
+
add_extension_entry(prefix, target_name)
|
|
88
107
|
end
|
|
89
108
|
|
|
90
109
|
# Parse Cargo.toml to extract target name
|
|
@@ -118,6 +137,21 @@ module Kompo
|
|
|
118
137
|
lib_name || package_name
|
|
119
138
|
end
|
|
120
139
|
|
|
140
|
+
# Parse Makefile to extract target_prefix and TARGET_NAME
|
|
141
|
+
# Returns [prefix, target_name] where prefix is empty string if not specified
|
|
142
|
+
def parse_makefile_metadata(makefile_content, fallback_name)
|
|
143
|
+
prefix = makefile_content.scan(/target_prefix = (.*)/).flatten.first&.delete_prefix("/") || ""
|
|
144
|
+
target_name = makefile_content.scan(/TARGET_NAME = (.*)/).flatten.first || fallback_name
|
|
145
|
+
[prefix, target_name]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Add extension entry to @exts for ruby_init_ext()
|
|
149
|
+
# ext_path must match the require path (without file extension)
|
|
150
|
+
def add_extension_entry(prefix, target_name)
|
|
151
|
+
ext_path = File.join(prefix, target_name).delete_prefix("/")
|
|
152
|
+
@exts << [ext_path, "Init_#{target_name}"]
|
|
153
|
+
end
|
|
154
|
+
|
|
121
155
|
def build_rust_extension(dir_name, cargo_toml, gem_ext_name, work_dir)
|
|
122
156
|
cargo = CargoPath.path
|
|
123
157
|
|
|
@@ -162,8 +196,7 @@ module Kompo
|
|
|
162
196
|
|
|
163
197
|
# Get full extension path (prefix/target_name) for proper directory structure
|
|
164
198
|
# This ensures erb/escape and cgi/escape are stored in different directories
|
|
165
|
-
prefix = makefile_content
|
|
166
|
-
target_name = makefile_content.scan(/TARGET_NAME = (.*)/).flatten.first || gem_ext_name
|
|
199
|
+
prefix, target_name = parse_makefile_metadata(makefile_content, gem_ext_name)
|
|
167
200
|
ext_path = File.join(prefix, target_name).delete_prefix("/")
|
|
168
201
|
dest_ext_dir = File.join(work_dir, "ext", ext_path)
|
|
169
202
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "digest"
|
|
5
|
+
require "json"
|
|
6
|
+
require "bundler"
|
|
7
7
|
|
|
8
8
|
module Kompo
|
|
9
9
|
# Shared helpers for bundle cache operations
|
|
@@ -19,7 +19,7 @@ module Kompo
|
|
|
19
19
|
|
|
20
20
|
def compute_gemfile_lock_hash
|
|
21
21
|
work_dir = WorkDir.path
|
|
22
|
-
gemfile_lock_path = File.join(work_dir,
|
|
22
|
+
gemfile_lock_path = File.join(work_dir, "Gemfile.lock")
|
|
23
23
|
return nil unless File.exist?(gemfile_lock_path)
|
|
24
24
|
|
|
25
25
|
content = File.read(gemfile_lock_path)
|
|
@@ -53,10 +53,10 @@ module Kompo
|
|
|
53
53
|
work_dir = WorkDir.path
|
|
54
54
|
ruby_major_minor = InstallRuby.ruby_major_minor
|
|
55
55
|
|
|
56
|
-
@bundle_ruby_dir = File.join(work_dir,
|
|
57
|
-
@bundler_config_path = File.join(work_dir,
|
|
56
|
+
@bundle_ruby_dir = File.join(work_dir, "bundle", "ruby", "#{ruby_major_minor}.0")
|
|
57
|
+
@bundler_config_path = File.join(work_dir, ".bundle", "config")
|
|
58
58
|
|
|
59
|
-
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path(
|
|
59
|
+
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path("~/.kompo/cache"))
|
|
60
60
|
ruby_version = InstallRuby.ruby_version
|
|
61
61
|
version_cache_dir = File.join(kompo_cache, ruby_version)
|
|
62
62
|
|
|
@@ -65,19 +65,19 @@ module Kompo
|
|
|
65
65
|
|
|
66
66
|
cache_dir = File.join(version_cache_dir, bundle_cache_name)
|
|
67
67
|
|
|
68
|
-
group(
|
|
68
|
+
group("Restoring bundle from cache") do
|
|
69
69
|
# Clean up existing files in case work_dir is reused
|
|
70
|
-
FileUtils.rm_rf(File.join(work_dir,
|
|
71
|
-
FileUtils.rm_rf(File.join(work_dir,
|
|
70
|
+
FileUtils.rm_rf(File.join(work_dir, "bundle")) if Dir.exist?(File.join(work_dir, "bundle"))
|
|
71
|
+
FileUtils.rm_rf(File.join(work_dir, ".bundle")) if Dir.exist?(File.join(work_dir, ".bundle"))
|
|
72
72
|
|
|
73
73
|
# Copy from cache
|
|
74
|
-
FileUtils.cp_r(File.join(cache_dir,
|
|
75
|
-
FileUtils.cp_r(File.join(cache_dir,
|
|
74
|
+
FileUtils.cp_r(File.join(cache_dir, "bundle"), File.join(work_dir, "bundle"))
|
|
75
|
+
FileUtils.cp_r(File.join(cache_dir, ".bundle"), File.join(work_dir, ".bundle"))
|
|
76
76
|
|
|
77
77
|
puts "Restored from: #{cache_dir}"
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
puts
|
|
80
|
+
puts "Bundle restored from cache"
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def clean
|
|
@@ -89,7 +89,7 @@ module Kompo
|
|
|
89
89
|
|
|
90
90
|
FileUtils.rm_rf(path) if File.exist?(path)
|
|
91
91
|
end
|
|
92
|
-
puts
|
|
92
|
+
puts "Cleaned up bundle installation"
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
@@ -102,33 +102,33 @@ module Kompo
|
|
|
102
102
|
bundler = InstallRuby.bundler_path
|
|
103
103
|
ruby_major_minor = InstallRuby.ruby_major_minor
|
|
104
104
|
|
|
105
|
-
@bundle_ruby_dir = File.join(work_dir,
|
|
106
|
-
@bundler_config_path = File.join(work_dir,
|
|
105
|
+
@bundle_ruby_dir = File.join(work_dir, "bundle", "ruby", "#{ruby_major_minor}.0")
|
|
106
|
+
@bundler_config_path = File.join(work_dir, ".bundle", "config")
|
|
107
107
|
|
|
108
|
-
puts
|
|
109
|
-
gemfile_path = File.join(work_dir,
|
|
108
|
+
puts "Running bundle install --path bundle..."
|
|
109
|
+
gemfile_path = File.join(work_dir, "Gemfile")
|
|
110
110
|
|
|
111
111
|
# Clear Bundler environment and specify Gemfile path explicitly
|
|
112
112
|
Bundler.with_unbundled_env do
|
|
113
113
|
ruby = InstallRuby.ruby_path
|
|
114
|
-
env = {
|
|
114
|
+
env = {"BUNDLE_GEMFILE" => gemfile_path}
|
|
115
115
|
|
|
116
116
|
# Suppress clang 18+ warning that causes mkmf try_cppflags to fail
|
|
117
117
|
# This flag is clang-specific and not recognized by GCC
|
|
118
118
|
if clang_compiler?
|
|
119
|
-
env[
|
|
120
|
-
env[
|
|
119
|
+
env["CFLAGS"] = "-Wno-default-const-init-field-unsafe"
|
|
120
|
+
env["CPPFLAGS"] = "-Wno-default-const-init-field-unsafe"
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
# Set BUNDLE_PATH to "bundle" - standard Bundler reads .bundle/config
|
|
124
124
|
# and finds gems in {BUNDLE_PATH}/ruby/X.X.X/gems/
|
|
125
125
|
# Use ruby to execute bundler to avoid shebang issues
|
|
126
|
-
system({
|
|
127
|
-
|
|
128
|
-
system(env, ruby, bundler,
|
|
126
|
+
system({"BUNDLE_GEMFILE" => gemfile_path}, ruby, bundler, "config", "set", "--local", "path",
|
|
127
|
+
"bundle") or raise "Failed to set bundle path"
|
|
128
|
+
system(env, ruby, bundler, "install") or raise "Failed to run bundle install"
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
puts
|
|
131
|
+
puts "Bundle installed successfully"
|
|
132
132
|
|
|
133
133
|
# Save to cache
|
|
134
134
|
save_to_cache(work_dir)
|
|
@@ -143,18 +143,18 @@ module Kompo
|
|
|
143
143
|
|
|
144
144
|
FileUtils.rm_rf(path) if File.exist?(path)
|
|
145
145
|
end
|
|
146
|
-
puts
|
|
146
|
+
puts "Cleaned up bundle installation"
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
private
|
|
150
150
|
|
|
151
151
|
def clang_compiler?
|
|
152
152
|
output = `cc --version 2>&1`
|
|
153
|
-
output.include?(
|
|
153
|
+
output.include?("clang")
|
|
154
154
|
rescue Errno::ENOENT => e
|
|
155
155
|
warn "cc command not found: #{e.message}"
|
|
156
156
|
false
|
|
157
|
-
rescue
|
|
157
|
+
rescue => e
|
|
158
158
|
warn "Error checking compiler: #{e.message}"
|
|
159
159
|
false
|
|
160
160
|
end
|
|
@@ -163,27 +163,27 @@ module Kompo
|
|
|
163
163
|
bundle_cache_name = compute_bundle_cache_name
|
|
164
164
|
return unless bundle_cache_name
|
|
165
165
|
|
|
166
|
-
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path(
|
|
166
|
+
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path("~/.kompo/cache"))
|
|
167
167
|
ruby_version = InstallRuby.ruby_version
|
|
168
168
|
version_cache_dir = File.join(kompo_cache, ruby_version)
|
|
169
169
|
cache_dir = File.join(version_cache_dir, bundle_cache_name)
|
|
170
170
|
|
|
171
|
-
group(
|
|
171
|
+
group("Saving bundle to cache") do
|
|
172
172
|
# Remove old cache if exists
|
|
173
173
|
FileUtils.rm_rf(cache_dir) if Dir.exist?(cache_dir)
|
|
174
174
|
FileUtils.mkdir_p(cache_dir)
|
|
175
175
|
|
|
176
176
|
# Copy to cache
|
|
177
|
-
FileUtils.cp_r(File.join(work_dir,
|
|
178
|
-
FileUtils.cp_r(File.join(work_dir,
|
|
177
|
+
FileUtils.cp_r(File.join(work_dir, "bundle"), File.join(cache_dir, "bundle"))
|
|
178
|
+
FileUtils.cp_r(File.join(work_dir, ".bundle"), File.join(cache_dir, ".bundle"))
|
|
179
179
|
|
|
180
180
|
# Save metadata
|
|
181
181
|
metadata = {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
"ruby_version" => ruby_version,
|
|
183
|
+
"gemfile_lock_hash" => compute_gemfile_lock_hash,
|
|
184
|
+
"created_at" => Time.now.iso8601
|
|
185
185
|
}
|
|
186
|
-
File.write(File.join(cache_dir,
|
|
186
|
+
File.write(File.join(cache_dir, "metadata.json"), JSON.pretty_generate(metadata))
|
|
187
187
|
|
|
188
188
|
puts "Saved to: #{cache_dir}"
|
|
189
189
|
end
|
|
@@ -193,7 +193,7 @@ module Kompo
|
|
|
193
193
|
# Skip bundle install when no Gemfile
|
|
194
194
|
class Skip < Taski::Task
|
|
195
195
|
def run
|
|
196
|
-
puts
|
|
196
|
+
puts "No Gemfile, skipping bundle install"
|
|
197
197
|
@bundle_ruby_dir = nil
|
|
198
198
|
@bundler_config_path = nil
|
|
199
199
|
end
|
|
@@ -209,14 +209,14 @@ module Kompo
|
|
|
209
209
|
bundle_cache_name = compute_bundle_cache_name
|
|
210
210
|
return false unless bundle_cache_name
|
|
211
211
|
|
|
212
|
-
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path(
|
|
212
|
+
kompo_cache = Taski.args.fetch(:kompo_cache, File.expand_path("~/.kompo/cache"))
|
|
213
213
|
ruby_version = InstallRuby.ruby_version
|
|
214
214
|
version_cache_dir = File.join(kompo_cache, ruby_version)
|
|
215
215
|
cache_dir = File.join(version_cache_dir, bundle_cache_name)
|
|
216
216
|
|
|
217
|
-
cache_bundle_dir = File.join(cache_dir,
|
|
218
|
-
cache_bundle_config = File.join(cache_dir,
|
|
219
|
-
cache_metadata = File.join(cache_dir,
|
|
217
|
+
cache_bundle_dir = File.join(cache_dir, "bundle")
|
|
218
|
+
cache_bundle_config = File.join(cache_dir, ".bundle")
|
|
219
|
+
cache_metadata = File.join(cache_dir, "metadata.json")
|
|
220
220
|
|
|
221
221
|
Dir.exist?(cache_bundle_dir) && Dir.exist?(cache_bundle_config) && File.exist?(cache_metadata)
|
|
222
222
|
end
|