Module: Tebako::Packager

Defined in:
lib/tebako/packager.rb,
lib/tebako/packager/patch.rb,
lib/tebako/packager/patch_main.rb,
lib/tebako/packager/pass1_patch.rb,
lib/tebako/packager/pass2_patch.rb,
lib/tebako/packager/pass1a_patch.rb,
lib/tebako/packager/patch_helpers.rb,
lib/tebako/packager/patch_literals.rb,
lib/tebako/packager/rubygems_patch.rb,
lib/tebako/packager/pass2_patch_crt.rb,
lib/tebako/packager/pass2msys_patch.rb,
lib/tebako/packager/patch_libraries.rb,
lib/tebako/packager/patch_buildsystem.rb

Overview

Packager module

Defined Under Namespace

Modules: PatchBuildsystem, PatchHelpers, PatchLibraries, PatchLiterals, PatchMain Classes: Pass1APatch, Pass1DarwinPatch, Pass1MSysPatch, Pass1Patch, Pass2MSysPatch, Pass2NonMSysPatch, Pass2Patch, Patch, RubygemsPatch, RubygemsUpdatePatch

Constant Summary collapse

FILES_TO_RESTORE =
%w[
  common.mk
  configure
  config.status
  dir.c
  dln.c
  file.c
  gem_prelude.rb
  io.c
  main.c
  Makefile
  ruby.c
  thread_pthread.c
  util.c
  ext/bigdecimal/bigdecimal.h
  ext/Setup
  cygwin/GNUmakefile.in
  include/ruby/onigmo.h
  lib/rubygems/openssl.rb
  lib/rubygems/path_support.rb
  template/Makefile.in
  tool/mkconfig.rb
  win32/winmain.c
  win32/file.c
].freeze
GNUMAKEFILE_IN_DLLTOOL_SUBST =

Substitutions for cygwin/Gnumakefile.in shared across Pass1 and Pass2

<<~SUBST
  $(Q) dlltool --output-lib=$(LIBRUBY) --output-def=tebako.def --export-all $(LIBRUBY_A) --output-exp=$(RUBY_EXP)   # tebako patched
SUBST
GNUMAKEFILE_IN_WINMAIN_SUBST =

This MSYS specific thing ensure compilation of winmain.c Did try to understand why it did not work out of the box

<<~SUBST
  RUBYDEF = $(DLL_BASE_NAME).def

  # Start of tebako patch
  WINMAINOBJ    = win32/winmain.$(OBJEXT)
  $(WINMAINOBJ): win32/winmain.c
  # End of tebako patch
SUBST
IO_C_MSYS_BASE_PATCH =
{
  "#define open	rb_w32_uopen" => "#define open(p, f, m) tebako_open(3, (p), (f), (m))"
}.freeze
IO_C_MSYS_PATCH_PRE_32 =
{
  "(rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil))" =>
      "((is_tebako_file_descriptor((fptr)->fd) || rb_w32_io_cancelable_p((fptr)->fd)) ? \\\n" \
      "Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil))"
}.freeze
IO_C_MSYS_PATCH =
{
  "(rb_w32_io_cancelable_p((fptr)->fd) ? Qnil : rb_io_wait(fptr->self, " \
  "RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT))" =>
      "((is_tebako_file_descriptor((fptr)->fd) || rb_w32_io_cancelable_p((fptr)->fd)) ? \\\n" \
      "Qnil : rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT))"
}.freeze
RUBY_C_MSYS_PATH_SUBST =
<<~SUBST
  /* -- Start of tebako patch -- */
          VALUE path = within_tebako_memfs(paths) ?
                          rb_str_new_cstr(paths) :
                          RUBY_RELATIVE(paths, len);
          /* -- End of tebako patch -- */
SUBST
RUBY_C_MSYS_PATCHES =
{
  "#ifndef MAXPATHLEN" => "#{PatchLiterals::C_FILE_SUBST_LESS}\n#ifndef MAXPATHLEN",
  "VALUE path = RUBY_RELATIVE(paths, len);" => RUBY_C_MSYS_PATH_SUBST
}.freeze
WIN32_FILE_C_MSYS_SUBST =
<<~SUBST
  /* -- Start of tebako patch -- */
    if (tebako_file_load_ok(path)) return ret;
      /* -- End of tebako patch -- */
  wpath = mbstr_to_wstr(CP_UTF8, path, -1, &len);
SUBST
WIN32_FILE_C_MSYS_PATCHES =
{
  "#ifndef INVALID_FILE_ATTRIBUTES" => "#{PatchLiterals::C_FILE_SUBST_LESS}\n#ifndef INVALID_FILE_ATTRIBUTES",
  "wpath = mbstr_to_wstr(CP_UTF8, path, -1, &len);" => WIN32_FILE_C_MSYS_SUBST
}.freeze
WIN32_WIN32_C_MSYS_SUBST =
<<~SUBST
  /* -- Start of tebako patch -- */
    if (is_tebako_cwd()) {
      char* tebako_cwd = tebako_getcwd(NULL,0);
      if (tebako_cwd == NULL) {
        errno = ENOMEM;
        return NULL;
      }
      len = strlen(tebako_cwd) + 1;

      if (buffer) {
        if (size < len) {
          free(tebako_cwd);
          errno = ERANGE;
          return NULL;
        }
      }
      else {
        buffer = (*alloc)(len, arg);
        if (!buffer) {
          free(tebako_cwd);
          errno = ENOMEM;
          return NULL;
        }
      }
      translate_char(tebako_cwd, (char)0x5c, '/', CP_UTF8);
      strcpy(buffer, tebako_cwd);
      free(tebako_cwd);
      return buffer;
    }
    /* -- End of tebako patch -- */

    len = GetCurrentDirectoryW(0, NULL);
SUBST
WIN32_WIN32_C_MSYS_PATCHES =
{
  "#if defined _MSC_VER && _MSC_VER <= 1200" =>
    "#{PatchLiterals::C_FILE_SUBST_LESS}\n#if defined _MSC_VER && _MSC_VER <= 1200",
  "len = GetCurrentDirectoryW(0, NULL);" => WIN32_WIN32_C_MSYS_SUBST,
  "clock_gettime(clockid_t clock_id, struct timespec *sp)" =>
    "_dummy_clock_gettime(clockid_t clock_id, struct timespec *sp)",
  "clock_getres(clockid_t clock_id, struct timespec *sp)" =>
    "_dummy_clock_getres(clockid_t clock_id, struct timespec *sp)"
}.freeze

Class Method Summary collapse

Class Method Details

.create_implib(src_dir, package_src_dir, app_name, ruby_ver) ⇒ Object

Create implib



74
75
76
77
78
79
80
81
# File 'lib/tebako/packager.rb', line 74

def create_implib(src_dir, package_src_dir, app_name, ruby_ver)
  a_name = File.basename(app_name, ".*")
  create_def(src_dir, a_name)
  puts "   ... creating Windows import library"
  params = ["dlltool", "-d", def_fname(src_dir, a_name), "-D", out_fname(a_name), "--output-lib",
            lib_fname(package_src_dir, ruby_ver)]
  BuildHelpers.run_with_capture(params)
end

.crt_pass1_patch(ostype, mount_point, ruby_ver) ⇒ Object



38
39
40
41
42
43
44
45
46
47
# File 'lib/tebako/packager/pass1_patch.rb', line 38

def crt_pass1_patch(ostype, mount_point, ruby_ver)
  scmb = ScenarioManagerBase.new(ostype)
  if scmb.macos?
    Pass1DarwinPatch.new(mount_point, ruby_ver)
  elsif scmb.msys?
    Pass1MSysPatch.new(mount_point, ruby_ver)
  else
    Pass1Patch.new(mount_point, ruby_ver)
  end
end

.crt_pass2_patch(ostype, deps_lib_dir, ruby_ver) ⇒ Object



36
37
38
39
40
41
42
43
# File 'lib/tebako/packager/pass2_patch_crt.rb', line 36

def crt_pass2_patch(ostype, deps_lib_dir, ruby_ver)
  scmb = ScenarioManagerBase.new(ostype)
  if scmb.msys?
    Pass2MSysPatch.new(ostype, deps_lib_dir, ruby_ver)
  else
    Pass2NonMSysPatch.new(ostype, deps_lib_dir, ruby_ver)
  end
end

.deploy(target_dir, pre_dir, ruby_ver, fs_root, fs_entrance, cwd, bundle_cache_dir = nil, native_gem_cache_dir = nil, install_runtime: true, tool_bin_dir: nil) ⇒ Object

Deploy



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/tebako/packager.rb', line 84

def deploy(target_dir, pre_dir, ruby_ver, fs_root, fs_entrance, cwd, # rubocop:disable Metrics/ParameterLists
           bundle_cache_dir = nil,
           native_gem_cache_dir = nil,
           install_runtime: true,
           tool_bin_dir: nil)
  puts "-- Running deploy script"

  arguments = [fs_root, fs_entrance, target_dir, pre_dir, bundle_cache_dir, native_gem_cache_dir]
  deploy_helper = if tool_bin_dir
                    Tebako::DeployHelper.new(*arguments, tool_bin_dir: tool_bin_dir)
                  else
                    Tebako::DeployHelper.new(*arguments)
                  end
  deploy_helper.configure(ruby_ver, cwd)
  install_runtime ? deploy_helper.deploy : deploy_helper.deploy(install_runtime: false)
  Tebako::Stripper.strip(deploy_helper, target_dir)
end

.deploy_runtime(target_dir, pre_dir, ruby_ver, fs_root, fs_entrance) ⇒ Object

rubocop:enable Metrics/ParameterLists



102
103
104
105
106
107
108
# File 'lib/tebako/packager.rb', line 102

def deploy_runtime(target_dir, pre_dir, ruby_ver, fs_root, fs_entrance)
  puts "-- Preparing reusable runtime deployment"
  deploy_helper = Tebako::DeployHelper.new(fs_root, fs_entrance, target_dir, pre_dir)
  deploy_helper.configure(ruby_ver, nil)
  deploy_helper.deploy_runtime
  Tebako::Stripper.strip(deploy_helper, target_dir)
end

.do_patch(patch_map, root) ⇒ Object



110
111
112
# File 'lib/tebako/packager.rb', line 110

def do_patch(patch_map, root)
  patch_map.each { |fname, mapping| PatchHelpers.patch_file("#{root}/#{fname}", mapping) }
end

.finalize(src_dir, app_name, ruby_ver, patchelf, output_type) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/tebako/packager.rb', line 114

def finalize(src_dir, app_name, ruby_ver, patchelf, output_type)
  puts "-- Running finalize script"

  RubyBuilder.new(ruby_ver, src_dir).target_build(output_type)
  exe_suffix = ScenarioManagerBase.new.exe_suffix
  src_name = File.join(src_dir, "ruby#{exe_suffix}")
  patchelf(src_name, patchelf)
  package_name = "#{app_name}#{exe_suffix}"
  # strip_or_copy(os_type, src_name, package_name)
  Tebako::Stripper.strip_file(src_name, package_name)
  puts "Created tebako #{output_type} at \"#{package_name}\""
end

.init(stash_dir, src_dir, pre_dir, bin_dir, preserve_bin: false) ⇒ Object

Init



128
129
130
131
132
133
# File 'lib/tebako/packager.rb', line 128

def init(stash_dir, src_dir, pre_dir, bin_dir, preserve_bin: false)
  puts "-- Running init script", "   ... creating packaging environment at #{src_dir}"
  PatchHelpers.recreate([src_dir, pre_dir])
  preserve_bin ? FileUtils.mkdir_p(bin_dir) : PatchHelpers.recreate(bin_dir)
  FileUtils.cp_r "#{stash_dir}/.", src_dir
end

.init_application(src_dir, pre_dir, bin_dir) ⇒ Object



135
136
137
138
139
# File 'lib/tebako/packager.rb', line 135

def init_application(src_dir, pre_dir, bin_dir)
  puts "-- Preparing application-only packaging environment"
  PatchHelpers.recreate([src_dir, pre_dir])
  FileUtils.mkdir_p(bin_dir)
end

.mkdwarfs(deps_bin_dir, data_bin_file, data_src_dir, descriptor = nil, compression_level = 5, cache_dir = nil) ⇒ Object

rubocop:disable Metrics/ParameterLists



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/tebako/packager.rb', line 141

def mkdwarfs(deps_bin_dir, data_bin_file, data_src_dir, descriptor = nil, # rubocop:disable Metrics/ParameterLists
             compression_level = 5, cache_dir = nil) # rubocop:enable Metrics/ParameterLists
  puts "-- Running mkdwarfs script"
  mkdwarfs = File.join(deps_bin_dir, "mkdwarfs")
  FileUtils.chmod("a+x", Dir.glob(File.join(deps_bin_dir, "mkdwarfs*")))
  return run_mkdwarfs(mkdwarfs, data_bin_file, data_src_dir, descriptor, compression_level) unless cache_dir

  cache = Tebako::FilesystemCache.new(cache_dir: cache_dir, mkdwarfs: mkdwarfs, source_dir: data_src_dir,
                                      descriptor: descriptor, compression_level: compression_level)
  cache.fetch(data_bin_file) do
    run_mkdwarfs(mkdwarfs, data_bin_file, data_src_dir, descriptor, compression_level)
  end
end

.pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver) ⇒ Object

Pass1 Executed before Ruby build, patching ensures that Ruby itself is linked statically



157
158
159
160
161
162
163
164
165
166
167
# File 'lib/tebako/packager.rb', line 157

def pass1(ostype, ruby_source_dir, mount_point, src_dir, ruby_ver)
  puts "-- Running pass1 script"
  PatchHelpers.recreate(src_dir)

  # Roll all known patches
  # Just in case we are recovering after some error
  PatchHelpers.restore_and_save_files(FILES_TO_RESTORE, ruby_source_dir, strict: false)

  patch = crt_pass1_patch(ostype, mount_point, ruby_ver)
  do_patch(patch.patch_map, ruby_source_dir)
end

.pass1a(ruby_source_dir) ⇒ Object

Pass1A Patch gem_prelude.rb



171
172
173
174
175
# File 'lib/tebako/packager.rb', line 171

def pass1a(ruby_source_dir)
  puts "-- Running pass1a script"
  patch = Pass1APatch.new
  do_patch(patch.patch_map, ruby_source_dir)
end

.pass2(ostype, ruby_source_dir, deps_lib_dir, ruby_ver) ⇒ Object

Pass2 Creates packaging environment, patching ensures that tebako package is linked statically



179
180
181
182
183
184
# File 'lib/tebako/packager.rb', line 179

def pass2(ostype, ruby_source_dir, deps_lib_dir, ruby_ver)
  puts "-- Running pass2 script"

  patch = crt_pass2_patch(ostype, deps_lib_dir, ruby_ver)
  do_patch(patch.patch_map, ruby_source_dir)
end

.stash(src_dir, stash_dir, ruby_source_dir, ruby_ver) ⇒ Object

Stash Created and saves pristine Ruby environment that is used to deploy applications for packaging



188
189
190
191
192
193
194
195
# File 'lib/tebako/packager.rb', line 188

def stash(src_dir, stash_dir, ruby_source_dir, ruby_ver)
  puts "-- Running stash script"
  RubyBuilder.new(ruby_ver, ruby_source_dir).toolchain_build

  puts "   ... saving pristine Ruby environment to #{stash_dir}"
  PatchHelpers.recreate(stash_dir)
  FileUtils.cp_r "#{src_dir}/.", stash_dir
end