summaryrefslogtreecommitdiff
path: root/tool/lib/bundled_gem.rb
blob: d2ed61a508fddea71cd8ce85095346d423982ae0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
require 'fileutils'
require 'rubygems'
require 'rubygems/package'

# This library is used by "make extract-gems" to
# unpack bundled gem files.

module BundledGem
  DEFAULT_GEMS_DEPENDENCIES = [
    "net-protocol", # net-ftp
    "time", # net-ftp
    "singleton", # prime
    "ipaddr", # rinda
    "forwardable", # prime, rinda
    "strscan", # rexml
    "psych" # rdoc
  ]

  module_function

  def unpack(file, *rest)
    pkg = Gem::Package.new(file)
    prepare_test(pkg.spec, *rest) do |dir|
      pkg.extract_files(dir)
      FileUtils.rm_rf(Dir.glob(".git*", base: dir).map {|n| File.join(dir, n)})
    end
    puts "Unpacked #{file}"
  rescue Gem::Package::FormatError, Errno::ENOENT
    puts "Try with hash version of bundled gems instead of #{file}. We don't use this gem with release version of Ruby."
    if file =~ /^gems\/(\w+)-/
      file = Dir.glob("gems/#{$1}-*.gem").first
    end
    retry
  end

  def build(gemspec, version, outdir = ".", validation: true)
    outdir = File.expand_path(outdir)
    gemdir, gemfile = File.split(gemspec)
    Dir.chdir(gemdir) do
      spec = Gem::Specification.load(gemfile)
      abort "Failed to load #{gemspec}" unless spec
      output = File.join(outdir, spec.file_name)
      FileUtils.rm_rf(output)
      package = Gem::Package.new(output)
      package.spec = spec
      package.build(validation == false)
    end
  end

  def copy(path, *rest)
    path, n = File.split(path)
    spec = Dir.chdir(path) {Gem::Specification.load(n)} or raise "Cannot load #{path}"
    prepare_test(spec, *rest) do |dir|
      FileUtils.rm_rf(dir)
      files = spec.files.reject {|f| f.start_with?(".git")}
      dirs = files.map {|f| File.dirname(f) if f.include?("/")}.uniq
      FileUtils.mkdir_p(dirs.map {|d| d ? "#{dir}/#{d}" : dir}.sort_by {|d| d.count("/")})
      files.each do |f|
        File.copy_stream(File.join(path, f), File.join(dir, f))
      end
    end
    puts "Copied #{path}"
  end

  def prepare_test(spec, dir = ".")
    target = spec.full_name
    Gem.ensure_gem_subdirectories(dir)
    gem_dir = File.join(dir, "gems", target)
    yield gem_dir
    spec_dir = spec.extensions.empty? ? "specifications" : File.join("gems", target)
    if spec.extensions.empty?
      spec.dependencies.reject! {|dep| DEFAULT_GEMS_DEPENDENCIES.include?(dep.name)}
    end
    File.binwrite(File.join(dir, spec_dir, "#{target}.gemspec"), spec.to_ruby)
    unless spec.extensions.empty?
      spec.dependencies.clear
      File.binwrite(File.join(dir, spec_dir, ".bundled.#{target}.gemspec"), spec.to_ruby)
    end
    if spec.bindir and spec.executables
      bindir = File.join(dir, "bin")
      Dir.mkdir(bindir) rescue nil
      spec.executables.each do |exe|
        File.open(File.join(bindir, exe), "wb", 0o777) {|f|
          f.print "#!ruby\n",
                  %[load File.realpath("../gems/#{target}/#{spec.bindir}/#{exe}", __dir__)\n]
        }
      end
    end
    FileUtils.rm_rf(Dir.glob("#{gem_dir}/.git*"))
  end

  def dummy_gemspec(gemspec)
    return if File.exist?(gemspec)
    gemdir, gemfile = File.split(gemspec)
    Dir.chdir(gemdir) do
      spec = Gem::Specification.new do |s|
        s.name = gemfile.chomp(".gemspec")
        s.version =
          File.read("lib/#{s.name}.rb")[/VERSION = "(.+?)"/, 1] ||
          begin File.read("lib/#{s.name}/version.rb")[/VERSION = "(.+?)"/, 1]; rescue; nil; end ||
          raise("cannot find the version of #{ s.name } gem")
        s.authors = ["DUMMY"]
        s.email = ["dummy@ruby-lang.org"]
        s.files = Dir.glob("{lib,ext}/**/*").select {|f| File.file?(f)}
        s.licenses = ["Ruby"]
        s.description = "DO NOT USE; dummy gemspec only for test"
        s.summary = "(dummy gemspec)"
      end
      File.write(gemfile, spec.to_ruby)
    end
  end

  def checkout(gemdir, repo, rev, git: $git)
    return unless rev or !git or git.empty?
    unless File.exist?("#{gemdir}/.git")
      puts "Cloning #{repo}"
      command = "#{git} clone #{repo} #{gemdir}"
      system(command) or raise "failed: #{command}"
    end
    puts "Update #{File.basename(gemdir)} to #{rev}"
    command = "#{git} fetch origin #{rev}"
    system(command, chdir: gemdir) or raise "failed: #{command}"
    command = "#{git} checkout --detach #{rev}"
    system(command, chdir: gemdir) or raise "failed: #{command}"
  end
end