dama 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +227 -0
- data/dama-logo.svg +91 -0
- data/exe/dama +4 -0
- data/ext/dama_native/.cargo/config.toml +3 -0
- data/ext/dama_native/Cargo.lock +3575 -0
- data/ext/dama_native/Cargo.toml +39 -0
- data/ext/dama_native/extconf.rb +72 -0
- data/ext/dama_native/src/audio.rs +134 -0
- data/ext/dama_native/src/engine.rs +339 -0
- data/ext/dama_native/src/lib.rs +396 -0
- data/ext/dama_native/src/renderer/screenshot.rs +84 -0
- data/ext/dama_native/src/renderer/shape_renderer.rs +507 -0
- data/ext/dama_native/src/renderer/text_renderer.rs +192 -0
- data/ext/dama_native/src/renderer.rs +563 -0
- data/ext/dama_native/src/window.rs +255 -0
- data/lib/dama/animation.rb +66 -0
- data/lib/dama/asset_cache.rb +56 -0
- data/lib/dama/audio.rb +47 -0
- data/lib/dama/auto_loader.rb +54 -0
- data/lib/dama/backend/base.rb +137 -0
- data/lib/dama/backend/native/ffi_bindings.rb +122 -0
- data/lib/dama/backend/native.rb +191 -0
- data/lib/dama/backend/web.rb +199 -0
- data/lib/dama/backend.rb +13 -0
- data/lib/dama/camera.rb +68 -0
- data/lib/dama/cli/new_project.rb +112 -0
- data/lib/dama/cli/release.rb +45 -0
- data/lib/dama/cli.rb +22 -0
- data/lib/dama/colors.rb +30 -0
- data/lib/dama/command_buffer.rb +83 -0
- data/lib/dama/component/attribute_definition.rb +13 -0
- data/lib/dama/component/attribute_set.rb +32 -0
- data/lib/dama/component.rb +28 -0
- data/lib/dama/configuration.rb +18 -0
- data/lib/dama/debug/frame_controller.rb +35 -0
- data/lib/dama/debug/screenshot_tool.rb +19 -0
- data/lib/dama/debug.rb +4 -0
- data/lib/dama/event_bus.rb +47 -0
- data/lib/dama/game/builder.rb +31 -0
- data/lib/dama/game/loop.rb +44 -0
- data/lib/dama/game.rb +88 -0
- data/lib/dama/geometry/circle.rb +28 -0
- data/lib/dama/geometry/rect.rb +16 -0
- data/lib/dama/geometry/sprite.rb +18 -0
- data/lib/dama/geometry/triangle.rb +13 -0
- data/lib/dama/geometry.rb +4 -0
- data/lib/dama/input/keyboard_state.rb +44 -0
- data/lib/dama/input/mouse_state.rb +45 -0
- data/lib/dama/input.rb +38 -0
- data/lib/dama/keys.rb +67 -0
- data/lib/dama/node/component_slot.rb +18 -0
- data/lib/dama/node/draw_context.rb +96 -0
- data/lib/dama/node.rb +139 -0
- data/lib/dama/physics/body.rb +57 -0
- data/lib/dama/physics/collider.rb +152 -0
- data/lib/dama/physics/collision.rb +15 -0
- data/lib/dama/physics/world.rb +125 -0
- data/lib/dama/physics.rb +4 -0
- data/lib/dama/registry/class_resolver.rb +48 -0
- data/lib/dama/registry.rb +21 -0
- data/lib/dama/release/archiver.rb +100 -0
- data/lib/dama/release/defaults/icon.icns +0 -0
- data/lib/dama/release/defaults/icon.ico +0 -0
- data/lib/dama/release/defaults/icon.png +0 -0
- data/lib/dama/release/dylib_relinker.rb +95 -0
- data/lib/dama/release/game_file_copier.rb +35 -0
- data/lib/dama/release/game_metadata.rb +61 -0
- data/lib/dama/release/icon_provider.rb +36 -0
- data/lib/dama/release/native_builder.rb +44 -0
- data/lib/dama/release/packager/linux.rb +62 -0
- data/lib/dama/release/packager/macos.rb +99 -0
- data/lib/dama/release/packager/web.rb +32 -0
- data/lib/dama/release/packager/windows.rb +61 -0
- data/lib/dama/release/packager.rb +9 -0
- data/lib/dama/release/platform_detector.rb +23 -0
- data/lib/dama/release/ruby_bundler.rb +163 -0
- data/lib/dama/release/stdlib_trimmer.rb +133 -0
- data/lib/dama/release/template_renderer.rb +40 -0
- data/lib/dama/release/templates/info_plist.xml.erb +19 -0
- data/lib/dama/release/templates/launcher_linux.sh.erb +10 -0
- data/lib/dama/release/templates/launcher_macos.sh.erb +10 -0
- data/lib/dama/release/templates/launcher_windows.bat.erb +11 -0
- data/lib/dama/release.rb +7 -0
- data/lib/dama/scene/composer.rb +65 -0
- data/lib/dama/scene.rb +233 -0
- data/lib/dama/scene_graph/class_index.rb +26 -0
- data/lib/dama/scene_graph/group_node.rb +27 -0
- data/lib/dama/scene_graph/instance_node.rb +30 -0
- data/lib/dama/scene_graph/path_selector.rb +25 -0
- data/lib/dama/scene_graph/query.rb +34 -0
- data/lib/dama/scene_graph/tag_index.rb +26 -0
- data/lib/dama/scene_graph/tree.rb +65 -0
- data/lib/dama/scene_graph.rb +4 -0
- data/lib/dama/sprite_sheet.rb +36 -0
- data/lib/dama/tween/easing.rb +31 -0
- data/lib/dama/tween/lerp.rb +35 -0
- data/lib/dama/tween/manager.rb +28 -0
- data/lib/dama/tween.rb +4 -0
- data/lib/dama/version.rb +3 -0
- data/lib/dama/vertex_batch.rb +35 -0
- data/lib/dama/web/entry.rb +79 -0
- data/lib/dama/web/static/index.html +142 -0
- data/lib/dama/web_builder.rb +232 -0
- data/lib/dama.rb +42 -0
- metadata +198 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module Dama
|
|
5
|
+
module Release
|
|
6
|
+
module Packager
|
|
7
|
+
# Creates a self-contained macOS .app bundle containing
|
|
8
|
+
# the Ruby runtime, gems, native library, game code, and assets.
|
|
9
|
+
class Macos
|
|
10
|
+
def initialize(project_root:)
|
|
11
|
+
@project_root = project_root
|
|
12
|
+
@metadata = GameMetadata.new(project_root:)
|
|
13
|
+
@icon_provider = IconProvider.new(project_root:, platform: :macos)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def package
|
|
17
|
+
native_library_path = NativeBuilder.new.build
|
|
18
|
+
prepare_app_structure
|
|
19
|
+
ruby_path = RubyBundler.new(destination: resources_path, project_root:).bundle
|
|
20
|
+
relink_dylibs(ruby_path:)
|
|
21
|
+
FileUtils.cp(native_library_path, resources_path)
|
|
22
|
+
GameFileCopier.new(project_root:, destination: resources_path).copy
|
|
23
|
+
copy_icon
|
|
24
|
+
write_info_plist
|
|
25
|
+
write_launcher_script(native_library_path:)
|
|
26
|
+
|
|
27
|
+
archive_path = Archiver.new(source_path: app_path).create_macos_zip
|
|
28
|
+
puts "macOS release created: #{archive_path}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :project_root, :metadata, :icon_provider
|
|
34
|
+
|
|
35
|
+
def app_name
|
|
36
|
+
"#{metadata.release_name}.app"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def app_path
|
|
40
|
+
File.join(project_root, "release", app_name)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def contents_path
|
|
44
|
+
File.join(app_path, "Contents")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resources_path
|
|
48
|
+
File.join(contents_path, "Resources")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def macos_path
|
|
52
|
+
File.join(contents_path, "MacOS")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def prepare_app_structure
|
|
56
|
+
FileUtils.rm_rf(app_path)
|
|
57
|
+
FileUtils.mkdir_p(macos_path)
|
|
58
|
+
FileUtils.mkdir_p(resources_path)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Rewrites hardcoded absolute dylib paths in the bundled Ruby binary
|
|
62
|
+
# to use @loader_path-relative references, so the .app works on any Mac.
|
|
63
|
+
def relink_dylibs(ruby_path:)
|
|
64
|
+
ruby_binary_name = RbConfig::CONFIG.fetch("ruby_install_name")
|
|
65
|
+
ruby_binary_path = File.join(ruby_path, "bin", ruby_binary_name)
|
|
66
|
+
lib_destination = File.join(ruby_path, "lib")
|
|
67
|
+
DylibRelinker.new(binary_path: ruby_binary_path, lib_destination:).relink
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def copy_icon
|
|
71
|
+
icon_source = icon_provider.icon_path
|
|
72
|
+
FileUtils.cp(icon_source, File.join(resources_path, "icon.icns")) if File.exist?(icon_source)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def write_info_plist
|
|
76
|
+
content = TemplateRenderer.new(
|
|
77
|
+
template_name: "info_plist.xml.erb",
|
|
78
|
+
variables: { escaped_title: CGI.escapeHTML(metadata.title) },
|
|
79
|
+
).render
|
|
80
|
+
File.write(File.join(contents_path, "Info.plist"), content)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def write_launcher_script(native_library_path:)
|
|
84
|
+
launcher_path = File.join(macos_path, "launch")
|
|
85
|
+
content = TemplateRenderer.new(
|
|
86
|
+
template_name: "launcher_macos.sh.erb",
|
|
87
|
+
variables: {
|
|
88
|
+
native_lib_name: File.basename(native_library_path),
|
|
89
|
+
ruby_version: RbConfig::CONFIG.fetch("ruby_version"),
|
|
90
|
+
ruby_arch: RbConfig::CONFIG.fetch("arch"),
|
|
91
|
+
},
|
|
92
|
+
).render
|
|
93
|
+
File.write(launcher_path, content)
|
|
94
|
+
FileUtils.chmod(0o755, launcher_path)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module Dama
|
|
4
|
+
module Release
|
|
5
|
+
module Packager
|
|
6
|
+
# Produces a web-deployable build by delegating to
|
|
7
|
+
# WebBuilder, then copying the output to release/web/.
|
|
8
|
+
class Web
|
|
9
|
+
def initialize(project_root:)
|
|
10
|
+
@project_root = project_root
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def package
|
|
14
|
+
builder = Dama::WebBuilder.new(project_root:)
|
|
15
|
+
builder.build
|
|
16
|
+
|
|
17
|
+
release_dir = File.join(project_root, "release", "web")
|
|
18
|
+
FileUtils.rm_rf(release_dir)
|
|
19
|
+
FileUtils.mkdir_p(File.dirname(release_dir))
|
|
20
|
+
FileUtils.cp_r(File.join(project_root, "dist"), release_dir)
|
|
21
|
+
|
|
22
|
+
archive_path = Archiver.new(source_path: release_dir).create_zip
|
|
23
|
+
puts "Web release created: #{archive_path}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :project_root
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module Dama
|
|
4
|
+
module Release
|
|
5
|
+
module Packager
|
|
6
|
+
# Creates a self-contained Windows directory with a .bat launcher,
|
|
7
|
+
# bundled Ruby, native .dll, game files, and assets.
|
|
8
|
+
class Windows
|
|
9
|
+
def initialize(project_root:)
|
|
10
|
+
@project_root = project_root
|
|
11
|
+
@metadata = GameMetadata.new(project_root:)
|
|
12
|
+
@icon_provider = IconProvider.new(project_root:, platform: :windows)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def package
|
|
16
|
+
native_library_path = NativeBuilder.new.build
|
|
17
|
+
prepare_structure
|
|
18
|
+
RubyBundler.new(destination: release_path, project_root:).bundle
|
|
19
|
+
FileUtils.cp(native_library_path, release_path)
|
|
20
|
+
GameFileCopier.new(project_root:, destination: release_path).copy
|
|
21
|
+
copy_icon
|
|
22
|
+
write_launcher_script(native_library_path:)
|
|
23
|
+
|
|
24
|
+
archive_path = Archiver.new(source_path: release_path).create_zip
|
|
25
|
+
puts "Windows release created: #{archive_path}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
attr_reader :project_root, :metadata, :icon_provider
|
|
31
|
+
|
|
32
|
+
def release_path
|
|
33
|
+
File.join(project_root, "release", metadata.release_name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def prepare_structure
|
|
37
|
+
FileUtils.rm_rf(release_path)
|
|
38
|
+
FileUtils.mkdir_p(release_path)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def copy_icon
|
|
42
|
+
icon_source = icon_provider.icon_path
|
|
43
|
+
FileUtils.cp(icon_source, File.join(release_path, "icon.ico")) if File.exist?(icon_source)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def write_launcher_script(native_library_path:)
|
|
47
|
+
launcher_path = File.join(release_path, "#{metadata.release_name}.bat")
|
|
48
|
+
content = TemplateRenderer.new(
|
|
49
|
+
template_name: "launcher_windows.bat.erb",
|
|
50
|
+
variables: {
|
|
51
|
+
native_lib_name: File.basename(native_library_path),
|
|
52
|
+
ruby_version: RbConfig::CONFIG.fetch("ruby_version"),
|
|
53
|
+
ruby_arch: RbConfig::CONFIG.fetch("arch"),
|
|
54
|
+
},
|
|
55
|
+
).render
|
|
56
|
+
File.write(launcher_path, content)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Dama
|
|
2
|
+
module Release
|
|
3
|
+
# Resolves the current OS into a platform symbol
|
|
4
|
+
# used to select the correct packager.
|
|
5
|
+
class PlatformDetector
|
|
6
|
+
PLATFORMS = {
|
|
7
|
+
"darwin" => :macos,
|
|
8
|
+
"linux" => :linux,
|
|
9
|
+
"mingw" => :windows,
|
|
10
|
+
"mswin" => :windows,
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
class UnsupportedPlatformError < StandardError; end
|
|
14
|
+
|
|
15
|
+
def self.detect
|
|
16
|
+
platform_key = PLATFORMS.keys.detect { |k| RUBY_PLATFORM.include?(k) }
|
|
17
|
+
raise UnsupportedPlatformError, "Unsupported platform: #{RUBY_PLATFORM}" unless platform_key
|
|
18
|
+
|
|
19
|
+
PLATFORMS.fetch(platform_key)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
require "bundler"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
module Dama
|
|
6
|
+
module Release
|
|
7
|
+
# Copies the Ruby runtime and gem dependencies into a release directory.
|
|
8
|
+
# Creates a self-contained Ruby environment so the packaged game
|
|
9
|
+
# can run without a system Ruby installation.
|
|
10
|
+
class RubyBundler
|
|
11
|
+
def initialize(destination:, project_root:)
|
|
12
|
+
@destination = destination
|
|
13
|
+
@project_root = project_root
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def bundle
|
|
17
|
+
copy_ruby_runtime
|
|
18
|
+
install_gems
|
|
19
|
+
ruby_destination
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader :destination, :project_root
|
|
25
|
+
|
|
26
|
+
def ruby_destination
|
|
27
|
+
File.join(destination, "ruby")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def copy_ruby_runtime
|
|
31
|
+
ruby_binary = RbConfig.ruby
|
|
32
|
+
dest_bindir = File.join(ruby_destination, "bin")
|
|
33
|
+
|
|
34
|
+
FileUtils.mkdir_p(dest_bindir)
|
|
35
|
+
FileUtils.cp(ruby_binary, File.join(dest_bindir, File.basename(ruby_binary)))
|
|
36
|
+
|
|
37
|
+
copy_shared_library
|
|
38
|
+
copy_ruby_stdlib
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Copies the Ruby shared library (e.g. libruby.3.4.dylib) into
|
|
42
|
+
# the bundle so the binary doesn't depend on the build machine's paths.
|
|
43
|
+
def copy_shared_library
|
|
44
|
+
shared_lib = RbConfig::CONFIG.fetch("LIBRUBY_SO")
|
|
45
|
+
return if shared_lib.empty?
|
|
46
|
+
|
|
47
|
+
source = File.join(RbConfig::CONFIG.fetch("libdir"), shared_lib)
|
|
48
|
+
return unless File.exist?(source)
|
|
49
|
+
|
|
50
|
+
dest_libdir = File.join(ruby_destination, "lib")
|
|
51
|
+
FileUtils.mkdir_p(dest_libdir)
|
|
52
|
+
FileUtils.cp(source, dest_libdir)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def copy_ruby_stdlib
|
|
56
|
+
rubylibdir = RbConfig::CONFIG.fetch("rubylibdir")
|
|
57
|
+
dest_libdir = File.join(ruby_destination, "lib", "ruby", RbConfig::CONFIG.fetch("ruby_version"))
|
|
58
|
+
FileUtils.mkdir_p(dest_libdir)
|
|
59
|
+
FileUtils.cp_r("#{rubylibdir}/.", dest_libdir)
|
|
60
|
+
|
|
61
|
+
archdir = RbConfig::CONFIG.fetch("archdir")
|
|
62
|
+
dest_archdir = File.join(dest_libdir, RbConfig::CONFIG.fetch("arch"))
|
|
63
|
+
FileUtils.mkdir_p(dest_archdir)
|
|
64
|
+
FileUtils.cp_r("#{archdir}/.", dest_archdir)
|
|
65
|
+
|
|
66
|
+
StdlibTrimmer.new(stdlib_dir: dest_libdir, arch_dir: dest_archdir).trim
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Runs bundle install using the project's original Gemfile so that
|
|
70
|
+
# relative path: references (e.g. path: "../..") resolve correctly
|
|
71
|
+
# against the project root, not the release directory.
|
|
72
|
+
# Uses env vars instead of CLI flags to avoid writing .bundle/config
|
|
73
|
+
# into the project directory. The --standalone flag generates a
|
|
74
|
+
# bundler/setup.rb that sets up load paths without Bundler at runtime.
|
|
75
|
+
def install_gems
|
|
76
|
+
vendor_dir = File.join(destination, "vendor", "bundle")
|
|
77
|
+
FileUtils.mkdir_p(vendor_dir)
|
|
78
|
+
|
|
79
|
+
gemfile = File.join(project_root, "Gemfile")
|
|
80
|
+
return unless File.exist?(gemfile)
|
|
81
|
+
|
|
82
|
+
Bundler.with_unbundled_env do
|
|
83
|
+
env = {
|
|
84
|
+
"BUNDLE_PATH" => vendor_dir,
|
|
85
|
+
"BUNDLE_GEMFILE" => gemfile,
|
|
86
|
+
}
|
|
87
|
+
success = system(env, "bundle", "install", "--standalone")
|
|
88
|
+
raise "Gem bundling failed" unless success
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
embed_path_gems(vendor_dir:, gemfile:)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Bundler's --standalone mode generates relative paths for path: gems
|
|
95
|
+
# (e.g. path: "../..") that point back to the build machine's source tree.
|
|
96
|
+
# For a portable app bundle, we copy each path gem's lib/ into the vendor
|
|
97
|
+
# directory and rewrite setup.rb so it references the local copy.
|
|
98
|
+
def embed_path_gems(vendor_dir:, gemfile:)
|
|
99
|
+
setup_rb = File.join(vendor_dir, "bundler", "setup.rb")
|
|
100
|
+
return unless File.exist?(setup_rb)
|
|
101
|
+
|
|
102
|
+
gems = path_gem_lines(gemfile:)
|
|
103
|
+
return if gems.empty?
|
|
104
|
+
|
|
105
|
+
source_to_name = copy_path_gems(gems:, vendor_dir:)
|
|
106
|
+
rewrite_setup_rb(
|
|
107
|
+
setup_rb:,
|
|
108
|
+
bundler_dir: File.join(vendor_dir, "bundler"),
|
|
109
|
+
source_to_name:,
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def copy_path_gems(gems:, vendor_dir:)
|
|
114
|
+
gems.to_h do |gem_name, source_lib|
|
|
115
|
+
embedded_lib = File.join(vendor_dir, "path_gems", gem_name, "lib")
|
|
116
|
+
FileUtils.mkdir_p(embedded_lib)
|
|
117
|
+
FileUtils.cp_r("#{source_lib}/.", embedded_lib)
|
|
118
|
+
[source_lib, gem_name]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Rewrites path-gem entries in setup.rb to reference the embedded copies.
|
|
123
|
+
# Identifies each line's target gem by resolving its relative path back to
|
|
124
|
+
# an absolute path and matching against known source lib directories.
|
|
125
|
+
# This handles multiple path gems correctly, unlike a generic regex gsub.
|
|
126
|
+
def rewrite_setup_rb(setup_rb:, bundler_dir:, source_to_name:)
|
|
127
|
+
content = File.readlines(setup_rb).map do |line|
|
|
128
|
+
gem_name = match_path_gem(line:, bundler_dir:, source_to_name:)
|
|
129
|
+
gem_name ? path_gem_load_line(gem_name:) : line
|
|
130
|
+
end.join
|
|
131
|
+
|
|
132
|
+
File.write(setup_rb, content)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Matches any $:.unshift line that loads from a #{__dir__}-relative path.
|
|
136
|
+
# The captured rel_path is resolved against the bundler directory to
|
|
137
|
+
# determine which path gem (if any) this line references.
|
|
138
|
+
SETUP_LOAD_PATTERN = %r{\A\$:\.\s*unshift\s+File\.expand_path\("\#\{__dir__\}/(?<rel_path>[^"]+)"\)\n?\z}
|
|
139
|
+
|
|
140
|
+
def match_path_gem(line:, bundler_dir:, source_to_name:)
|
|
141
|
+
match = line.match(SETUP_LOAD_PATTERN)
|
|
142
|
+
return unless match
|
|
143
|
+
|
|
144
|
+
resolved = File.expand_path(match[:rel_path], bundler_dir)
|
|
145
|
+
source_to_name[resolved]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def path_gem_load_line(gem_name:)
|
|
149
|
+
"$:.unshift File.expand_path(\"\#{__dir__}/../path_gems/#{gem_name}/lib\")\n"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def path_gem_lines(gemfile:)
|
|
153
|
+
File.readlines(gemfile).filter_map do |line|
|
|
154
|
+
match = line.match(/gem\s+"(?<name>[^"]+)".*path:\s*"(?<path>[^"]+)"/)
|
|
155
|
+
next unless match
|
|
156
|
+
|
|
157
|
+
gem_path = File.expand_path(match[:path], File.dirname(gemfile))
|
|
158
|
+
[match[:name], File.join(gem_path, "lib")]
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module Dama
|
|
4
|
+
module Release
|
|
5
|
+
# Removes stdlib modules and native extensions that games don't
|
|
6
|
+
# need at runtime, reducing the bundled Ruby size by ~60%.
|
|
7
|
+
# Uses an exclusion-based approach (safer than a whitelist) so
|
|
8
|
+
# new stdlib additions aren't accidentally removed.
|
|
9
|
+
class StdlibTrimmer
|
|
10
|
+
# Pure-Ruby stdlib directories that are build tools, documentation
|
|
11
|
+
# generators, or parser infrastructure -- never needed at game runtime.
|
|
12
|
+
# Networking (net/, openssl/) and other libraries are intentionally
|
|
13
|
+
# kept since games may use them for multiplayer, asset loading, etc.
|
|
14
|
+
EXCLUDED_DIRS = %w[
|
|
15
|
+
bundler
|
|
16
|
+
did_you_mean
|
|
17
|
+
error_highlight
|
|
18
|
+
irb
|
|
19
|
+
prism
|
|
20
|
+
racc
|
|
21
|
+
rbs
|
|
22
|
+
rdoc
|
|
23
|
+
reline
|
|
24
|
+
ripper
|
|
25
|
+
ruby_vm
|
|
26
|
+
rubygems
|
|
27
|
+
syntax_suggest
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
# Top-level .rb files that correspond to excluded directories
|
|
31
|
+
# or standalone modules not needed at runtime.
|
|
32
|
+
EXCLUDED_FILES = %w[
|
|
33
|
+
bundler.rb
|
|
34
|
+
bundled_gems.rb
|
|
35
|
+
debug.rb
|
|
36
|
+
did_you_mean.rb
|
|
37
|
+
error_highlight.rb
|
|
38
|
+
irb.rb
|
|
39
|
+
mkmf.rb
|
|
40
|
+
prism.rb
|
|
41
|
+
rdoc.rb
|
|
42
|
+
reline.rb
|
|
43
|
+
ripper.rb
|
|
44
|
+
rubygems.rb
|
|
45
|
+
syntax_suggest.rb
|
|
46
|
+
].freeze
|
|
47
|
+
|
|
48
|
+
# Native extensions for build/dev-only tools.
|
|
49
|
+
EXCLUDED_NATIVE_EXTENSIONS = %w[
|
|
50
|
+
ripper
|
|
51
|
+
].freeze
|
|
52
|
+
|
|
53
|
+
def initialize(stdlib_dir:, arch_dir:)
|
|
54
|
+
@stdlib_dir = stdlib_dir
|
|
55
|
+
@arch_dir = arch_dir
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def trim
|
|
59
|
+
remove_excluded_dirs
|
|
60
|
+
remove_excluded_files
|
|
61
|
+
remove_excluded_native_extensions
|
|
62
|
+
remove_debug_symbols
|
|
63
|
+
trim_encodings
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
attr_reader :stdlib_dir, :arch_dir
|
|
69
|
+
|
|
70
|
+
def remove_excluded_dirs
|
|
71
|
+
EXCLUDED_DIRS.each do |dir|
|
|
72
|
+
FileUtils.rm_rf(File.join(stdlib_dir, dir))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def remove_excluded_files
|
|
77
|
+
EXCLUDED_FILES.each do |file|
|
|
78
|
+
FileUtils.rm_f(File.join(stdlib_dir, file))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def remove_excluded_native_extensions
|
|
83
|
+
EXCLUDED_NATIVE_EXTENSIONS.each do |name|
|
|
84
|
+
Dir.glob(File.join(arch_dir, "#{name}.*")).each { |f| FileUtils.rm_rf(f) }
|
|
85
|
+
FileUtils.rm_rf(File.join(arch_dir, name))
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Debug symbol directories (.dSYM) are macOS build artifacts
|
|
90
|
+
# that add megabytes without any runtime benefit.
|
|
91
|
+
def remove_debug_symbols
|
|
92
|
+
Dir.glob(File.join(arch_dir, "**", "*.dSYM")).each do |dsym|
|
|
93
|
+
FileUtils.rm_rf(dsym)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Ruby's encoding directory contains bundles for dozens of legacy
|
|
98
|
+
# character encodings. Games only need the encoding database
|
|
99
|
+
# (encdb) and transcoding database (transdb) that Ruby loads at boot,
|
|
100
|
+
# plus UTF-8 which is already compiled into the Ruby binary.
|
|
101
|
+
def trim_encodings
|
|
102
|
+
enc_dir = File.join(arch_dir, "enc")
|
|
103
|
+
return unless File.directory?(enc_dir)
|
|
104
|
+
|
|
105
|
+
keep_encoding_essentials(enc_dir:)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def keep_encoding_essentials(enc_dir:)
|
|
109
|
+
trans_dir = File.join(enc_dir, "trans")
|
|
110
|
+
|
|
111
|
+
essential_files = [
|
|
112
|
+
File.join(enc_dir, "encdb.bundle"),
|
|
113
|
+
File.join(enc_dir, "encdb.so"),
|
|
114
|
+
].select { |f| File.exist?(f) }
|
|
115
|
+
|
|
116
|
+
essential_trans = [
|
|
117
|
+
File.join(trans_dir, "transdb.bundle"),
|
|
118
|
+
File.join(trans_dir, "transdb.so"),
|
|
119
|
+
].select { |f| File.exist?(f) }
|
|
120
|
+
|
|
121
|
+
# Remove everything except the essential encoding files
|
|
122
|
+
non_essential_entries(enc_dir:, essential_files:).each { |e| FileUtils.rm_rf(e) }
|
|
123
|
+
non_essential_entries(enc_dir: trans_dir, essential_files: essential_trans).each { |e| FileUtils.rm_rf(e) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def non_essential_entries(enc_dir:, essential_files:)
|
|
127
|
+
Dir.children(enc_dir)
|
|
128
|
+
.map { |child| File.join(enc_dir, child) }
|
|
129
|
+
.reject { |path| essential_files.include?(path) || File.directory?(path) }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
|
|
3
|
+
module Dama
|
|
4
|
+
module Release
|
|
5
|
+
# Renders ERB templates from the templates/ directory.
|
|
6
|
+
# Separates template content (shell scripts, XML plists) from
|
|
7
|
+
# Ruby packaging logic so each can be edited independently.
|
|
8
|
+
class TemplateRenderer
|
|
9
|
+
TEMPLATES_DIR = File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
def initialize(template_name:, variables:)
|
|
12
|
+
@template_name = template_name
|
|
13
|
+
@variables = variables
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render
|
|
17
|
+
template = ERB.new(template_content, trim_mode: "-")
|
|
18
|
+
template.result(template_binding)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :template_name, :variables
|
|
24
|
+
|
|
25
|
+
def template_content
|
|
26
|
+
File.read(File.join(TEMPLATES_DIR, template_name))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Builds a clean binding where each variable key becomes
|
|
30
|
+
# a local method, keeping templates simple and explicit.
|
|
31
|
+
def template_binding
|
|
32
|
+
namespace = Object.new
|
|
33
|
+
variables.each do |key, value|
|
|
34
|
+
namespace.define_singleton_method(key) { value }
|
|
35
|
+
end
|
|
36
|
+
namespace.instance_eval { binding }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
3
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4
|
+
<plist version="1.0">
|
|
5
|
+
<dict>
|
|
6
|
+
<key>CFBundleName</key>
|
|
7
|
+
<string><%= escaped_title %></string>
|
|
8
|
+
<key>CFBundleDisplayName</key>
|
|
9
|
+
<string><%= escaped_title %></string>
|
|
10
|
+
<key>CFBundleExecutable</key>
|
|
11
|
+
<string>launch</string>
|
|
12
|
+
<key>CFBundleIconFile</key>
|
|
13
|
+
<string>icon</string>
|
|
14
|
+
<key>CFBundlePackageType</key>
|
|
15
|
+
<string>APPL</string>
|
|
16
|
+
<key>NSHighResolutionCapable</key>
|
|
17
|
+
<true/>
|
|
18
|
+
</dict>
|
|
19
|
+
</plist>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
3
|
+
|
|
4
|
+
export DAMA_NATIVE_LIB="$DIR/<%= native_lib_name %>"
|
|
5
|
+
export GEM_PATH="$DIR/vendor/bundle"
|
|
6
|
+
export BUNDLE_GEMFILE="$DIR/Gemfile"
|
|
7
|
+
export RUBYLIB="$DIR/ruby/lib/ruby/<%= ruby_version %>:$DIR/ruby/lib/ruby/<%= ruby_version %>/<%= ruby_arch %>"
|
|
8
|
+
|
|
9
|
+
exec "$DIR/ruby/bin/ruby" -r "$DIR/vendor/bundle/bundler/setup.rb" \
|
|
10
|
+
-e "require 'dama'; Dama.boot(root: \"$DIR\")"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
DIR="$(cd "$(dirname "$0")/../Resources" && pwd)"
|
|
3
|
+
|
|
4
|
+
export DAMA_NATIVE_LIB="$DIR/<%= native_lib_name %>"
|
|
5
|
+
export GEM_PATH="$DIR/vendor/bundle"
|
|
6
|
+
export BUNDLE_GEMFILE="$DIR/Gemfile"
|
|
7
|
+
export RUBYLIB="$DIR/ruby/lib/ruby/<%= ruby_version %>:$DIR/ruby/lib/ruby/<%= ruby_version %>/<%= ruby_arch %>"
|
|
8
|
+
|
|
9
|
+
exec "$DIR/ruby/bin/ruby" -r "$DIR/vendor/bundle/bundler/setup.rb" \
|
|
10
|
+
-e "require 'dama'; Dama.boot(root: \"$DIR\")"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
set DIR=%~dp0
|
|
3
|
+
if "%DIR:~-1%"=="\" set DIR=%DIR:~0,-1%
|
|
4
|
+
|
|
5
|
+
set DAMA_NATIVE_LIB=%DIR%\<%= native_lib_name %>
|
|
6
|
+
set GEM_PATH=%DIR%\vendor\bundle
|
|
7
|
+
set BUNDLE_GEMFILE=%DIR%\Gemfile
|
|
8
|
+
set RUBYLIB=%DIR%\ruby\lib\ruby\<%= ruby_version %>;%DIR%\ruby\lib\ruby\<%= ruby_version %>\<%= ruby_arch %>
|
|
9
|
+
|
|
10
|
+
"%DIR%\ruby\bin\ruby.exe" -r "%DIR%\vendor\bundle\bundler\setup.rb" ^
|
|
11
|
+
-e "require 'dama'; Dama.boot(root: '%DIR%')"
|