| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Module::Patch; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
775936
|
use 5.010001; |
|
|
2
|
|
|
|
|
9
|
|
|
4
|
2
|
|
|
2
|
|
12
|
use strict 'subs', 'vars'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
137
|
|
|
5
|
2
|
|
|
2
|
|
15
|
use warnings; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
162
|
|
|
6
|
2
|
|
|
2
|
|
3914
|
use Log::ger; |
|
|
2
|
|
|
|
|
134
|
|
|
|
2
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
1673
|
use Monkey::Patch::Action qw(); |
|
|
2
|
|
|
|
|
9388
|
|
|
|
2
|
|
|
|
|
60
|
|
|
9
|
2
|
|
|
2
|
|
1160
|
use Package::Stash; |
|
|
2
|
|
|
|
|
18828
|
|
|
|
2
|
|
|
|
|
106
|
|
|
10
|
2
|
|
|
2
|
|
893
|
use Package::Util::Lite qw(package_exists); |
|
|
2
|
|
|
|
|
1398
|
|
|
|
2
|
|
|
|
|
1863
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY |
|
13
|
|
|
|
|
|
|
our $DATE = '2024-04-17'; # DATE |
|
14
|
|
|
|
|
|
|
our $DIST = 'Module-Patch'; # DIST |
|
15
|
|
|
|
|
|
|
our $VERSION = '0.278'; # VERSION |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our @EXPORT_OK = qw(patch_package); |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub is_loaded { |
|
20
|
6
|
|
|
6
|
0
|
28
|
my $mod = shift; |
|
21
|
|
|
|
|
|
|
|
|
22
|
6
|
|
|
|
|
25
|
(my $mod_pm = "$mod.pm") =~ s!::!/!g; |
|
23
|
6
|
100
|
|
|
|
43
|
exists($INC{$mod_pm}) && $INC{$mod_pm}; |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my%loaded_by_us; |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub import { |
|
29
|
6
|
|
|
6
|
|
18812
|
my $self = shift; |
|
30
|
|
|
|
|
|
|
|
|
31
|
6
|
|
|
|
|
24
|
my $caller = caller; |
|
32
|
|
|
|
|
|
|
|
|
33
|
6
|
50
|
|
|
|
240
|
if ($self eq __PACKAGE__) { |
|
34
|
|
|
|
|
|
|
# we are not subclassed, provide exports |
|
35
|
0
|
|
|
|
|
0
|
for my $exp (@_) { |
|
36
|
|
|
|
|
|
|
die "$exp is not exported by ".__PACKAGE__ |
|
37
|
0
|
0
|
|
|
|
0
|
unless grep { $_ eq $exp } @EXPORT_OK; |
|
|
0
|
|
|
|
|
0
|
|
|
38
|
0
|
|
|
|
|
0
|
*{"$caller\::$exp"} = \&{$exp}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
} else { |
|
41
|
|
|
|
|
|
|
# patch already applied, ignore |
|
42
|
6
|
50
|
|
|
|
9
|
return if ${"$self\::handles"}; |
|
|
6
|
|
|
|
|
85
|
|
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# get patch data from patch_data() |
|
45
|
6
|
100
|
|
|
|
12
|
unless (${"$self\::patch_data_cached"}) { |
|
|
6
|
|
|
|
|
34
|
|
|
46
|
3
|
|
|
|
|
14
|
${"$self\::patch_data_cached"} = $self->patch_data; |
|
|
3
|
|
|
|
|
48
|
|
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# we are subclassed, patch caller with patch data |
|
50
|
6
|
|
|
|
|
17
|
my %opts = @_; |
|
51
|
|
|
|
|
|
|
|
|
52
|
6
|
|
|
|
|
12
|
my $load; |
|
53
|
6
|
50
|
|
|
|
24
|
if (exists $opts{-load_target}) { |
|
54
|
0
|
|
|
|
|
0
|
$load = $opts{-load_target}; |
|
55
|
0
|
|
|
|
|
0
|
delete $opts{-load_target}; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
6
|
|
33
|
|
|
21
|
$load //= ${"$self\::patch_data_cached"}->{-load_target}; |
|
|
6
|
|
|
|
|
64
|
|
|
58
|
6
|
|
50
|
|
|
43
|
$load //= 1; |
|
59
|
|
|
|
|
|
|
|
|
60
|
6
|
|
|
|
|
10
|
my $force; |
|
61
|
6
|
100
|
|
|
|
19
|
if (exists $opts{-force}) { |
|
62
|
1
|
|
|
|
|
4
|
$force = $opts{-force}; |
|
63
|
1
|
|
|
|
|
4
|
delete $opts{-force}; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
6
|
|
66
|
|
|
27
|
$force //= ${"$self\::patch_data_cached"}->{-force}; |
|
|
5
|
|
|
|
|
23
|
|
|
66
|
6
|
|
100
|
|
|
21
|
$force //= 0; |
|
67
|
|
|
|
|
|
|
|
|
68
|
6
|
|
|
|
|
9
|
my $warn; |
|
69
|
6
|
50
|
|
|
|
14
|
if (exists $opts{-warn_target_loaded}) { |
|
70
|
0
|
|
|
|
|
0
|
$warn = $opts{-warn_target_loaded}; |
|
71
|
0
|
|
|
|
|
0
|
delete $opts{-warn_target_loaded}; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
6
|
|
33
|
|
|
17
|
$warn //= ${"$self\::patch_data_cached"}->{"-warn_target_loaded"}; |
|
|
6
|
|
|
|
|
29
|
|
|
74
|
6
|
|
50
|
|
|
22
|
$warn //= 1; |
|
75
|
|
|
|
|
|
|
|
|
76
|
6
|
50
|
|
|
|
9
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
|
6
|
|
|
|
|
24
|
|
|
77
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
|
78
|
6
|
|
50
|
|
|
19
|
my $v = $pdata->{v} // 1; |
|
79
|
6
|
|
|
|
|
11
|
my $curv = 3; |
|
80
|
6
|
50
|
33
|
|
|
34
|
if ($v == 1 || $v == 2) { |
|
|
|
50
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
0
|
my $mpv; |
|
82
|
0
|
0
|
|
|
|
0
|
if ($v == 1) { |
|
|
|
0
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
0
|
$mpv = "0.06 or earlier"; |
|
84
|
|
|
|
|
|
|
} elsif ($v == 2) { |
|
85
|
0
|
|
|
|
|
0
|
$mpv = "0.07-0.09"; |
|
86
|
|
|
|
|
|
|
} |
|
87
|
0
|
|
0
|
|
|
0
|
die "$self ".( ${"$self\::VERSION" } // "?" ). |
|
|
0
|
|
0
|
|
|
0
|
|
|
88
|
|
|
|
|
|
|
" requires Module::Patch $mpv (patch_data format v=$v),". |
|
89
|
|
|
|
|
|
|
" this is Module::Patch ".($Module::Patch::VERSION // '?'). |
|
90
|
|
|
|
|
|
|
" (v=$curv), please install an older version of ". |
|
91
|
|
|
|
|
|
|
"Module::Patch or upgrade $self"; |
|
92
|
|
|
|
|
|
|
} elsif ($v == 3) { |
|
93
|
|
|
|
|
|
|
# ok, current version |
|
94
|
|
|
|
|
|
|
} else { |
|
95
|
0
|
|
|
|
|
0
|
die "BUG: $self: Unknown patch_data format version ($v), ". |
|
96
|
|
|
|
|
|
|
"only v=$curv supported by this version of Module::Patch"; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
6
|
|
|
|
|
13
|
my $target = $self; |
|
100
|
6
|
50
|
|
|
|
56
|
$target =~ s/(?<=\w)::[Pp]atch::\w+$// |
|
101
|
|
|
|
|
|
|
or die "BUG: $self: Bad patch module name '$target', it should ". |
|
102
|
|
|
|
|
|
|
"end with '::Patch::YourCategory'"; |
|
103
|
|
|
|
|
|
|
|
|
104
|
6
|
100
|
|
|
|
20
|
if (is_loaded($target)) { |
|
105
|
5
|
50
|
|
|
|
16
|
if (!$loaded_by_us{$target}) { |
|
106
|
0
|
0
|
0
|
|
|
0
|
if ($load && $warn) { |
|
107
|
0
|
|
|
|
|
0
|
warn "$target is loaded before ".__PACKAGE__.", this is ". |
|
108
|
|
|
|
|
|
|
"not recommended since $target might export subs ". |
|
109
|
|
|
|
|
|
|
"before " . __PACKAGE__." gets the chance to patch " . |
|
110
|
|
|
|
|
|
|
"them"; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
} else { |
|
114
|
1
|
50
|
|
|
|
3
|
if ($load) { |
|
115
|
1
|
|
|
1
|
|
102
|
eval "package $caller; use $target"; ## no critic: BuiltinFunctions::ProhibitStringyEval |
|
|
1
|
|
|
|
|
553
|
|
|
|
1
|
|
|
|
|
47
|
|
|
|
1
|
|
|
|
|
29
|
|
|
116
|
1
|
50
|
|
|
|
6
|
die if $@; |
|
117
|
1
|
|
|
|
|
3
|
$loaded_by_us{$target}++; |
|
118
|
|
|
|
|
|
|
} else { |
|
119
|
0
|
0
|
|
|
|
0
|
if ($warn) { |
|
120
|
0
|
|
|
|
|
0
|
warn "$target does not exist and we are told not to load ". |
|
121
|
|
|
|
|
|
|
"it, skipped patching"; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
0
|
|
|
|
|
0
|
return; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# read patch module's configs |
|
128
|
2
|
|
|
2
|
|
16
|
no warnings 'once'; |
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
3308
|
|
|
129
|
6
|
|
100
|
|
|
24
|
my $pcdata = $pdata->{config} // {}; |
|
130
|
6
|
|
|
|
|
12
|
my $config = \%{"$self\::config"}; |
|
|
6
|
|
|
|
|
28
|
|
|
131
|
6
|
|
|
|
|
28
|
while (my ($k, $v) = each %$pcdata) { |
|
132
|
6
|
50
|
|
|
|
24
|
die "Invalid configuration defined by $self\::patch_data(): ". |
|
133
|
|
|
|
|
|
|
"$k: must start with dash" unless $k =~ /\A-/; |
|
134
|
6
|
|
|
|
|
16
|
$config->{$k} = $v->{default}; |
|
135
|
6
|
100
|
|
|
|
87
|
if (exists $opts{$k}) { |
|
136
|
1
|
|
|
|
|
2
|
$config->{$k} = $opts{$k}; |
|
137
|
1
|
|
|
|
|
5
|
delete $opts{$k}; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
6
|
100
|
|
|
|
18
|
if (keys %opts) { |
|
142
|
1
|
|
|
|
|
24
|
die "$self: Unknown option(s): ".join(", ", keys %opts); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
5
|
50
|
|
|
|
14
|
if ($pdata->{after_read_config}) { |
|
146
|
0
|
|
|
|
|
0
|
$pdata->{after_read_config}->(); |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
5
|
50
|
|
|
|
13
|
if ($pdata->{before_patch}) { |
|
150
|
0
|
|
|
|
|
0
|
$pdata->{before_patch}->(); |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
5
|
|
|
|
|
27
|
log_trace "Module::Patch: patching $target with $self ..."; |
|
154
|
4
|
|
|
|
|
20
|
${"$self\::handles"} = patch_package( |
|
155
|
|
|
|
|
|
|
$target, $pdata->{patches}, |
|
156
|
5
|
|
33
|
|
|
67
|
{force=>$force, patch_module=>ref($self) || $self}); |
|
157
|
|
|
|
|
|
|
|
|
158
|
4
|
50
|
|
|
|
39
|
if ($pdata->{after_patch}) { |
|
159
|
0
|
|
|
|
|
0
|
$pdata->{after_patch}->(); |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub unimport { |
|
166
|
4
|
|
|
4
|
|
10823
|
my $self = shift; |
|
167
|
|
|
|
|
|
|
|
|
168
|
4
|
50
|
|
|
|
16
|
if ($self eq __PACKAGE__) { |
|
169
|
|
|
|
|
|
|
# we are not subclassed, do nothing |
|
170
|
|
|
|
|
|
|
} else { |
|
171
|
4
|
50
|
|
|
|
6
|
my $pdata = ${"$self\::patch_data_cached"} or |
|
|
4
|
|
|
|
|
50
|
|
|
172
|
|
|
|
|
|
|
die "BUG: $self: No patch data supplied"; |
|
173
|
|
|
|
|
|
|
|
|
174
|
4
|
50
|
|
|
|
20
|
if ($pdata->{before_unpatch}) { |
|
175
|
0
|
|
|
|
|
0
|
$pdata->{before_unpatch}->(); |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
4
|
|
|
|
|
16
|
my $handles = ${"$self\::handles"}; |
|
|
4
|
|
|
|
|
15
|
|
|
179
|
4
|
|
|
|
|
20
|
log_trace "Module::Patch: Unpatching $self ..."; |
|
180
|
4
|
|
|
|
|
15
|
undef ${"$self\::handles"}; |
|
|
4
|
|
|
|
|
16
|
|
|
181
|
|
|
|
|
|
|
# do we need to undef ${"$self\::config"}?, i'm thinking not really |
|
182
|
|
|
|
|
|
|
|
|
183
|
4
|
50
|
|
|
|
29
|
if ($pdata->{after_unpatch}) { |
|
184
|
0
|
|
|
|
|
0
|
$pdata->{after_unpatch}->(); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub patch_data { |
|
191
|
0
|
|
|
0
|
0
|
0
|
die "BUG: patch_data() should be provided by subclass"; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub patch_package { |
|
195
|
5
|
|
|
5
|
1
|
48
|
my ($package0, $patches_spec, $opts) = @_; |
|
196
|
5
|
|
50
|
|
|
13
|
$opts //= {}; |
|
197
|
|
|
|
|
|
|
|
|
198
|
5
|
|
|
|
|
10
|
my $handles = {}; |
|
199
|
5
|
50
|
|
|
|
21
|
for my $target (ref($package0) eq 'ARRAY' ? @$package0 : ($package0)) { |
|
200
|
5
|
50
|
|
|
|
21
|
die "FATAL: Target module '$target' not loaded" |
|
201
|
|
|
|
|
|
|
unless package_exists($target); |
|
202
|
5
|
|
|
|
|
96
|
my $target_version = ${"$target\::VERSION"}; |
|
|
5
|
|
|
|
|
18
|
|
|
203
|
5
|
|
|
|
|
5
|
my $target_subs; |
|
204
|
|
|
|
|
|
|
|
|
205
|
5
|
|
|
|
|
10
|
my $i = 0; |
|
206
|
|
|
|
|
|
|
PATCH: |
|
207
|
5
|
|
|
|
|
11
|
for my $pspec (@$patches_spec) { |
|
208
|
7
|
|
|
|
|
13
|
my $act = $pspec->{action}; |
|
209
|
7
|
50
|
|
|
|
28
|
my $errp = ($opts->{patch_module} ? "$opts->{patch_module}: ":""). |
|
210
|
|
|
|
|
|
|
"patch[$i]"; # error prefix |
|
211
|
7
|
50
|
|
|
|
20
|
$act or die "BUG: $errp: no action supplied"; |
|
212
|
7
|
50
|
|
|
|
38
|
$act =~ /\A(wrap|add|replace|add_or_replace|delete)\z/ or die |
|
213
|
|
|
|
|
|
|
"BUG: $errp: action '$pspec->{action}' unknown"; |
|
214
|
7
|
50
|
|
|
|
19
|
if ($act eq 'delete') { |
|
215
|
0
|
0
|
|
|
|
0
|
$pspec->{code} and die "BUG: $errp: for action 'delete', ". |
|
216
|
|
|
|
|
|
|
"code must not be supplied"; |
|
217
|
|
|
|
|
|
|
} else { |
|
218
|
7
|
50
|
|
|
|
17
|
$pspec->{code} or die "BUG: $errp: code not supplied"; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
my $sub_names = ref($pspec->{sub_name}) eq 'ARRAY' ? |
|
222
|
7
|
50
|
|
|
|
25
|
[@{ $pspec->{sub_name} }] : [$pspec->{sub_name}]; |
|
|
0
|
|
|
|
|
0
|
|
|
223
|
7
|
|
|
|
|
16
|
for (@$sub_names) { |
|
224
|
7
|
50
|
|
|
|
19
|
$_ = qr/.*/ if $_ eq ':all'; |
|
225
|
7
|
50
|
|
|
|
15
|
$_ = qr/^_/ if $_ eq ':private'; |
|
226
|
7
|
50
|
|
|
|
15
|
$_ = qr/^[^_]/ if $_ eq ':public'; |
|
227
|
7
|
50
|
|
|
|
20
|
die "BUG: $errp: unknown tag in sub_name $_" if /^:/; |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
7
|
|
|
|
|
13
|
my @s; |
|
231
|
7
|
|
|
|
|
10
|
for my $sub_name (@$sub_names) { |
|
232
|
7
|
50
|
|
|
|
15
|
if (ref($sub_name) eq 'Regexp') { |
|
233
|
0
|
0
|
|
|
|
0
|
unless ($target_subs) { |
|
234
|
0
|
|
|
|
|
0
|
$target_subs = [Package::Stash->new($target)->list_all_symbols("CODE")]; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
0
|
|
|
|
|
0
|
for (@$target_subs) { |
|
237
|
0
|
0
|
0
|
|
|
0
|
push @s, $_ if $_ !~~ @s && $_ =~ $sub_name; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
} else { |
|
240
|
7
|
|
|
|
|
47
|
push @s, $sub_name; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
7
|
100
|
66
|
|
|
36
|
unless (!defined($pspec->{mod_version}) || |
|
245
|
|
|
|
|
|
|
$pspec->{mod_version} eq ':all') { |
|
246
|
5
|
50
|
33
|
|
|
21
|
defined($target_version) && length($target_version) |
|
247
|
|
|
|
|
|
|
or die "FATAL: Target package '$target' does not have ". |
|
248
|
|
|
|
|
|
|
"\$VERSION"; |
|
249
|
5
|
|
|
|
|
9
|
my $mod_versions = $pspec->{mod_version}; |
|
250
|
5
|
100
|
|
|
|
33
|
$mod_versions = ref($mod_versions) eq 'ARRAY' ? |
|
251
|
|
|
|
|
|
|
[@$mod_versions] : [$mod_versions]; |
|
252
|
5
|
|
|
|
|
12
|
for (@$mod_versions) { |
|
253
|
7
|
50
|
|
|
|
19
|
$_ = qr/.*/ if $_ eq ':all'; |
|
254
|
7
|
50
|
|
|
|
18
|
die "BUG: $errp: unknown tag in mod_version $_" |
|
255
|
|
|
|
|
|
|
if /^:/; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
5
|
100
|
|
|
|
13
|
unless (grep { |
|
259
|
7
|
50
|
|
|
|
39
|
ref($_) eq 'Regexp' ? $target_version =~ $_ : $target_version eq $_ |
|
260
|
|
|
|
|
|
|
} @$mod_versions) { |
|
261
|
|
|
|
|
|
|
warn "$errp: Target module version $target_version ". |
|
262
|
|
|
|
|
|
|
"does not match [".join(", ", @$mod_versions)."], ". |
|
263
|
|
|
|
|
|
|
($opts->{force} ? |
|
264
|
2
|
100
|
|
|
|
114
|
"patching anyway (force)":"skipped"). "."; |
|
265
|
2
|
100
|
|
|
|
37
|
next PATCH unless $opts->{force}; |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
6
|
|
|
|
|
15
|
for my $s (@s) { |
|
270
|
|
|
|
|
|
|
#log_trace("Patching %s ...", $s); |
|
271
|
|
|
|
|
|
|
$handles->{"$target\::$s"} = |
|
272
|
|
|
|
|
|
|
Monkey::Patch::Action::patch_package( |
|
273
|
6
|
|
|
|
|
25
|
$target, $s, $act, $pspec->{code}); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
5
|
|
|
|
|
560
|
$i++; |
|
277
|
|
|
|
|
|
|
} # for $pspec |
|
278
|
|
|
|
|
|
|
} # for $target |
|
279
|
4
|
|
|
|
|
10
|
$handles; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |
|
283
|
|
|
|
|
|
|
# ABSTRACT: Patch package with a set of patches |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
__END__ |