Class: Ocran::ZipPayloadBuilder

Inherits:
Object
  • Object
show all
Includes:
BuildConstants
Defined in:
lib/ocran/zip_payload_builder.rb

Overview

Builder that packages an application by INJECTING it into the ZIP store of a cosmopolitan Ruby APE, instead of building a launcher stub that unpacks a temporary directory at every start.

A CosmoRuby build auto-runs the member /zip/main.rb of its own archive when there is one, passing the command line through as ARGV. So a complete application is: a byte-for-byte copy of ruby.com, plus the application's files, plus a generated main.rb that prepares the environment and loads the real script. Nothing is compiled at packaging time (no cosmocc) and nothing is written to disk at run time (no extraction, no temp directory).

Layout inside the archive:

/zip/main.rb                generated bootstrap, the entry point
/zip/ocran/src/...          the application's own files
/zip/ocran/gems/...         packed pure-Ruby gems (GEM_HOME/GEM_PATH)
/zip/ocran/lib/ruby/...     files packed relative to the Ruby prefix

Everything except main.rb lives under the "ocran/" prefix because the interpreter's own standard library already occupies /zip/lib/ruby and /zip/bin: a shared namespace would let a packed file shadow part of the interpreter. The prefix makes collisions structurally impossible, and it is the same tree the extraction mode would have written to a temp directory, so Direction needs no separate layout.

Constant Summary collapse

ZIP_ROOT =

Where the interpreter maps its embedded archive.

"/zip"
APP_ROOT =

Root of the packed application inside the archive.

"#{ZIP_ROOT}/ocran"
MAIN_SCRIPT =

Archive member the interpreter runs on startup.

"main.rb"

Constants included from BuildConstants

BuildConstants::BINDIR, BuildConstants::EXTRACT_ROOT, BuildConstants::GEMDIR, BuildConstants::LIBDIR, BuildConstants::SRCDIR

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, cosmo_ruby:, chdir_before: false, debug_mode: false) {|_self| ... } ⇒ ZipPayloadBuilder

Returns a new instance of ZipPayloadBuilder.

Yields:

  • (_self)

Yield Parameters:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/ocran/zip_payload_builder.rb', line 46

def initialize(path, cosmo_ruby:, chdir_before: false, debug_mode: false)
  @path = Pathname(path)
  @cosmo_ruby = Pathname(cosmo_ruby)
  @chdir_before = chdir_before
  @debug_mode = debug_mode
  @entries = []
  @names = {}
  @env = {}
  @exec_args = nil
  @ignored_symlinks = []
  @data_size = 0

  yield(self) if block_given?

  finalize
end

Instance Attribute Details

#data_sizeObject (readonly)

Uncompressed size of everything packed, for the build summary.



44
45
46
# File 'lib/ocran/zip_payload_builder.rb', line 44

def data_size
  @data_size
end

Symlinks the extraction mode would create (libruby aliases). A ZIP member cannot be a symlink zipos would follow, and none are needed: the packed interpreter is a single static binary.



66
67
68
# File 'lib/ocran/zip_payload_builder.rb', line 66

def ignored_symlinks
  @ignored_symlinks
end

Class Method Details

.parse_rubyopt(rubyopt) ⇒ Object

Splits a RUBYOPT string into the -I directories and -r libraries that can be replayed from inside the script, and the flags that cannot. Returns [includes, requires, unsupported].



268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/ocran/zip_payload_builder.rb', line 268

def self.parse_rubyopt(rubyopt)
  includes, requires, unsupported = [], [], []

  rubyopt.split(/\s+/).reject(&:empty?).each do |token|
    case token
    when /\A-I(.+)\z/ then includes << $1
    when /\A-r(.+)\z/ then requires << $1
    else unsupported << token
    end
  end

  [includes, requires, unsupported]
end

Instance Method Details

#cp(source, target) ⇒ Object



72
73
74
75
76
77
# File 'lib/ocran/zip_payload_builder.rb', line 72

def cp(source, target)
  source = source.to_s
  add(Entry.new(name: archive_name(target), source: source, mode: file_mode(source),
                mtime: File.mtime(source)))
  @data_size += File.size(source)
end

#exec(image, script, *argv) ⇒ Object

The image argument is the packed interpreter the extraction mode would spawn; here the running interpreter IS that binary, so only the script and its arguments matter.



90
91
92
93
# File 'lib/ocran/zip_payload_builder.rb', line 90

def exec(image, script, *argv)
  raise "Script is already set" if @exec_args
  @exec_args = [in_archive(image), in_archive(script), argv.map { |arg| replace_root(arg.to_s) }]
end

#export(name, value) ⇒ Object



83
84
85
# File 'lib/ocran/zip_payload_builder.rb', line 83

def export(name, value)
  @env[name.to_s] = replace_root(value.to_s)
end

#mkdir(target) ⇒ Object



68
69
70
# File 'lib/ocran/zip_payload_builder.rb', line 68

def mkdir(target)
  add(Entry.new(name: "#{archive_name(target)}/"))
end


79
80
81
# File 'lib/ocran/zip_payload_builder.rb', line 79

def symlink(link_path, target)
  @ignored_symlinks << [link_path.to_s, target.to_s]
end