Class: Gem::Patcher

Inherits:
Object
  • Object
show all
Includes:
UserInteraction
Defined in:
lib/rubygems/patcher.rb

Overview

Gem::Patcher is used to patch .gem files by calling system patch command

Defined Under Namespace

Classes: PatchCommandMissing

Instance Method Summary collapse

Constructor Details

#initialize(gemfile, output_dir) ⇒ Patcher

Returns a new instance of Patcher.



18
19
20
21
22
23
24
25
26
# File 'lib/rubygems/patcher.rb', line 18

def initialize(gemfile, output_dir)
  @gemfile    = gemfile
  @output_dir = output_dir

  # @target_dir is a temporary directory where the gem files live
  tmpdir      = Dir.mktmpdir
  basename    = File.basename(gemfile, '.gem')
  @target_dir = File.join(tmpdir, basename)
end

Instance Method Details

#apply_patch(patch, options) ⇒ Object

Apply one patch at a time using options

Default options:

options[:strip] = 1
options[:fuzz]  = 2


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
# File 'lib/rubygems/patcher.rb', line 62

def apply_patch(patch, options)
  options[:strip] ||= 1
  options[:fuzz]  ||= 2
  
  patch_path = File.expand_path(patch)
  info 'Path to the patch to apply: ' + patch_path
  copy_in(options[:copy_in], @target_dir) if options[:copy_in]

  # Apply the patch by calling 'patch -pNUMBER < patch'
  Dir.chdir @target_dir do
    opts = ["--verbose",  "-p#{options[:strip]}", "--fuzz=#{options[:fuzz]}", "#{options[:patch_options]}"]
    IO.popen("patch #{opts.join(' ')} < #{patch_path} 2>&1") do |out|
      std = out.readlines
      out.close
      info std

      unless $?.nil?
        if $?.exitstatus == 0
          @output << "Succesfully patched with #{patch}"
        else
          @output << "Error: Unable to patch with #{patch}."

          unless Gem.configuration.really_verbose
            @output << 'Run gem patch with --verbose option to swich to verbose mode.'
          end
        end
      end
	std
    end
  end
end

#failed?Boolean

Return false if any of the pathes failed

Returns:

  • (Boolean)


122
123
124
# File 'lib/rubygems/patcher.rb', line 122

def failed?
  @std.join(' ') =~ /.*Hunk #[0-9]+ (ignored|failed).*/
end

#outputObject

Return output lines



107
108
109
# File 'lib/rubygems/patcher.rb', line 107

def output
  @output
end

#patch_with(patches, options) ⇒ Object

Patch the gem, move the new patched gem to options[:outfile] and return the path



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rubygems/patcher.rb', line 32

def patch_with(patches, options)
  @std, @output = [], []

  check_patch_command_is_installed
  extract_gem
  copy_in(options[:copy_in], @target_dir) if options[:copy_in]

  # Apply all patches
  patches.each do |patch|
    info 'Applying patch ' + patch
    @std << apply_patch(patch, options)
  end
  remove(options[:remove], @target_dir) if options[:remove]
  # Rebuild only if there weren't any problems
  build_patched_gem unless failed?

  options[:outfile] ||= File.join(@output_dir, @package.spec.file_name)
  FileUtils.mv((File.join @target_dir, @package.spec.file_name), options[:outfile]) unless options[:dry_run]

  # Return the path to the patched gem
  options[:outfile]
end

Print results from patching if Gem.configuration.really_verbose



98
99
100
101
102
# File 'lib/rubygems/patcher.rb', line 98

def print_results
  @output.each do |msg|
    say msg 
  end
end

#stdObject

Return standard output from patch command



115
116
117
# File 'lib/rubygems/patcher.rb', line 115

def std
  @std
end