line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################# |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Manipulate (and probably abuse) a RPM spec |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Author: Chris Weyl (cpan:RSRCHBOY), <cweyl@alumni.drew.edu> |
6
|
|
|
|
|
|
|
# Company: No company, personal work |
7
|
|
|
|
|
|
|
# Created: 05/04/2009 09:36:47 PM PDT |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# Copyright (c) 2009 Chris Weyl <cweyl@alumni.drew.edu> |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# This library is free software; you can redistribute it and/or |
12
|
|
|
|
|
|
|
# modify it under the terms of the GNU Lesser General Public |
13
|
|
|
|
|
|
|
# License as published by the Free Software Foundation; either |
14
|
|
|
|
|
|
|
# version 2.1 of the License, or (at your option) any later version. |
15
|
|
|
|
|
|
|
# |
16
|
|
|
|
|
|
|
############################################################################# |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package RPM::Spec; |
19
|
|
|
|
|
|
|
|
20
|
1
|
|
|
1
|
|
30761
|
use Moose; |
|
1
|
|
|
|
|
729389
|
|
|
1
|
|
|
|
|
9
|
|
21
|
1
|
|
|
1
|
|
7940
|
use namespace::autoclean; |
|
1
|
|
|
|
|
1372
|
|
|
1
|
|
|
|
|
6
|
|
22
|
|
|
|
|
|
|
|
23
|
1
|
|
|
1
|
|
762
|
use MooseX::Types::Moose ':all'; |
|
1
|
|
|
|
|
66116
|
|
|
1
|
|
|
|
|
10
|
|
24
|
1
|
|
|
1
|
|
9349
|
use MooseX::Types::Path::Class ':all'; |
|
1
|
|
|
|
|
82007
|
|
|
1
|
|
|
|
|
9
|
|
25
|
|
|
|
|
|
|
|
26
|
1
|
|
|
1
|
|
1518
|
use Path::Class; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1345
|
|
27
|
|
|
|
|
|
|
#use RPM::Spec::DependencyInfo; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
our $VERSION = '0.05'; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# debugging |
32
|
|
|
|
|
|
|
#use Smart::Comments '###', '####'; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
############################################################################# |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
has specfile => (is => 'ro', isa => File, coerce => 1, required => 1); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
has filter_magic => (is => 'ro', isa => Bool, coerce => 1, default => 1); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
############################################################################# |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
has _content => ( |
43
|
|
|
|
|
|
|
traits => [ 'Array' ], is => 'ro', isa => 'ArrayRef[Str]', lazy_build => 1, |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
handles => { |
46
|
|
|
|
|
|
|
has_content => 'count', |
47
|
|
|
|
|
|
|
num_lines_in_content => 'count', |
48
|
|
|
|
|
|
|
grep_content => 'grep', |
49
|
|
|
|
|
|
|
content => 'elements', |
50
|
|
|
|
|
|
|
}, |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
|
53
|
0
|
|
|
0
|
|
|
sub _build__content { [ split /\n/, shift->specfile->slurp ] } |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has license => (is => 'ro', isa => 'Str', lazy_build => 1); |
56
|
|
|
|
|
|
|
has epoch => (is => 'ro', isa => 'Maybe[Str]', lazy_build => 1); |
57
|
|
|
|
|
|
|
has version => (is => 'ro', isa => 'Str', lazy_build => 1); |
58
|
|
|
|
|
|
|
has release => (is => 'ro', isa => 'Str', lazy_build => 1); |
59
|
|
|
|
|
|
|
has summary => (is => 'ro', isa => 'Str', lazy_build => 1); |
60
|
|
|
|
|
|
|
has source0 => (is => 'ro', isa => 'Str', lazy_build => 1); |
61
|
|
|
|
|
|
|
has name => (is => 'ro', isa => 'Str', lazy_build => 1); |
62
|
|
|
|
|
|
|
# FIXME should we be a Uri type? |
63
|
|
|
|
|
|
|
has url => (is => 'ro', isa => 'Str', lazy_build => 1); |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
0
|
|
|
sub _build_license { shift->_find(sub { /^License:/i }) } |
|
0
|
|
|
0
|
|
|
|
66
|
0
|
|
|
0
|
|
|
sub _build_epoch { shift->_find(sub { /^Epoch:/i }) } |
|
0
|
|
|
0
|
|
|
|
67
|
0
|
|
|
0
|
|
|
sub _build_version { shift->_find(sub { /^Version:/i }) } |
|
0
|
|
|
0
|
|
|
|
68
|
0
|
|
|
0
|
|
|
sub _build_release { shift->_find(sub { /^Release:/i }) } |
|
0
|
|
|
0
|
|
|
|
69
|
0
|
|
|
0
|
|
|
sub _build_summary { shift->_find(sub { /^Summary:/i }) } |
|
0
|
|
|
0
|
|
|
|
70
|
0
|
|
|
0
|
|
|
sub _build_source0 { shift->_find(sub { /^Source(0|):/i }) } |
|
0
|
|
|
0
|
|
|
|
71
|
0
|
|
|
0
|
|
|
sub _build_name { shift->_find(sub { /^Name:/i }) } |
|
0
|
|
|
0
|
|
|
|
72
|
0
|
|
|
0
|
|
|
sub _build_url { shift->_find(sub { /^URL:/i }) } |
|
0
|
|
|
0
|
|
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# LineToken: value_returned |
75
|
0
|
|
|
0
|
|
|
sub _find { (split /\s+/, (shift->grep_content(shift))[0], 2)[1] } |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
has _build_requires => ( |
78
|
|
|
|
|
|
|
traits => [ 'Hash' ], is => 'ro', isa => 'HashRef', lazy_build => 1, |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
handles => { |
81
|
|
|
|
|
|
|
has_build_requires => 'count', |
82
|
|
|
|
|
|
|
has_build_require => 'exists', |
83
|
|
|
|
|
|
|
build_require_version => 'get', |
84
|
|
|
|
|
|
|
num_build_requires => 'count', |
85
|
|
|
|
|
|
|
build_requires => 'keys', |
86
|
|
|
|
|
|
|
full_build_requires => 'elements', |
87
|
|
|
|
|
|
|
}, |
88
|
|
|
|
|
|
|
); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _build__build_requires { |
91
|
0
|
|
|
0
|
|
|
my $self = shift @_; |
92
|
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
my %brs = |
94
|
0
|
0
|
|
|
|
|
map { my @p = split /\s+/, $_; $p[0] => $p[2] ? $p[2] : 0 } |
|
0
|
|
|
|
|
|
|
95
|
0
|
|
|
|
|
|
map { $_ =~ s/^BuildRequires:\s*//; $_ } |
96
|
0
|
|
|
0
|
|
|
$self->grep_content(sub { /^BuildRequires:/i } ) |
97
|
0
|
|
|
|
|
|
; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
### %brs |
100
|
0
|
|
|
|
|
|
return \%brs; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
has _requires => ( |
104
|
|
|
|
|
|
|
traits => [ 'Hash' ], is => 'ro', isa => 'HashRef', lazy_build => 1, |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
handles => { |
107
|
|
|
|
|
|
|
has_requires => 'count', |
108
|
|
|
|
|
|
|
has_require => 'exists', |
109
|
|
|
|
|
|
|
require_version => 'get', |
110
|
|
|
|
|
|
|
num_requires => 'count', |
111
|
|
|
|
|
|
|
requires => 'keys', |
112
|
|
|
|
|
|
|
full_requires => 'elements', |
113
|
|
|
|
|
|
|
}, |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _build__requires { |
117
|
0
|
|
|
0
|
|
|
my $self = shift @_; |
118
|
|
|
|
|
|
|
|
119
|
0
|
|
|
|
|
|
my %brs = |
120
|
0
|
0
|
|
|
|
|
map { my @p = split /\s+/, $_; $p[0] => $p[2] ? $p[2] : 0 } |
|
0
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
map { $_ =~ s/^Requires:\s*//; $_ } |
122
|
0
|
|
|
0
|
|
|
$self->grep_content(sub { /^Requires:/i } ) |
123
|
0
|
|
|
|
|
|
; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# only one "magic" requires at the moment |
126
|
0
|
0
|
|
|
|
|
delete $brs{'perl(:MODULE_COMPAT_%(eval'} if $self->filter_magic; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
### %brs |
129
|
0
|
|
|
|
|
|
return \%brs; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
has _middle => ( |
133
|
|
|
|
|
|
|
traits => ['Array'], is => 'ro', isa => 'ArrayRef[Str]', lazy_build => 1, |
134
|
|
|
|
|
|
|
handles => { middle => 'elements' }, |
135
|
|
|
|
|
|
|
); |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
0
|
|
|
sub _build__middle { [ _after('%description', _before('%changelog', shift->content)) ] } |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
has _changelog => ( |
140
|
|
|
|
|
|
|
traits => ['Array'], is => 'ro', isa => 'ArrayRef[Str]', lazy_build => 1, |
141
|
|
|
|
|
|
|
handles => { |
142
|
|
|
|
|
|
|
has_changelog => 'count', |
143
|
|
|
|
|
|
|
grep_changelog => 'grep', |
144
|
|
|
|
|
|
|
num_lines_in_changelog => 'count', |
145
|
|
|
|
|
|
|
changelog => 'elements', |
146
|
|
|
|
|
|
|
}, |
147
|
|
|
|
|
|
|
); |
148
|
|
|
|
|
|
|
|
149
|
0
|
|
|
0
|
|
|
sub _build__changelog { [ _after('%changelog', shift->content) ] } |
150
|
|
|
|
|
|
|
|
151
|
0
|
0
|
|
0
|
|
|
sub _before { my $sep = shift @_; my @l; do { return @l if /^$sep/; push @l, $_ } for @_ } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
152
|
0
|
|
|
0
|
|
|
sub _after { reverse _before(shift, reverse @_) } |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
__END__ |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head1 NAME |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
RPM::Spec - A very simplistic read-only method of accessing RPM spec files |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 SYNOPSIS |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
use RPM::Spec; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $spec = RPM::Spec->new('path/to/my.spec'); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
say 'Version is: ' . $spec->version; |
170
|
|
|
|
|
|
|
say 'Spec has an epoch' if $spec->has_epoch; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 DESCRIPTION |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
B<WARNING: This code is actively being worked on, and the API may change.> |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
RPM::Spec provides simplistic access to the different bits of information a |
177
|
|
|
|
|
|
|
spec file provides... It is basically a collection of different parsing |
178
|
|
|
|
|
|
|
routines that were scattered through a bunch of other modules. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head1 CLASS FUNCTIONS |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=over 4 |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=item B<new(specfile =E<gt> [Str|File])> |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Create a new RPM::Specfile object. The only required parameter is 'specfile', |
187
|
|
|
|
|
|
|
which is either a string or L<Path::Class::File> object pointing to the |
188
|
|
|
|
|
|
|
location of the specfile. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=back |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head1 METHODS |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head2 Tag Functions |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
These methods each return the value of the given tag. If the tag is not |
197
|
|
|
|
|
|
|
present, undef is returned. |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=over 4 |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item B<license> |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item B<epoch> |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item B<release> |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item B<version> |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item B<source0> |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Note this will pick up from either of "Source" or "Source0" tags. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item B<name> |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item B<url> |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item B<summary> |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item B<middle> |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
The "middle" of a spec; e.g. everything from the first %description until the |
222
|
|
|
|
|
|
|
changelog starts. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=back |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head2 Dependency Functions |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Documentation as this interface is likely to change in the Very Near Future. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 Content Functions |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=over 4 |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item B<content()> |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Returns an array of strings representing the specfile. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item B<has_content> |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Is the file empty? |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item B<num_lines_in_content> |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Returns the line count of the specfile. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item B<grep_content(sub { ... })> |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Given a coderef, greps through the content with it. See also L<grep>. |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=back |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head1 CAVEATS |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head2 No macro parsing |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Macros are not evaluated. e.g., if "Release: 1%{?dist}" is in your spec file, |
257
|
|
|
|
|
|
|
"1%{?dist}" will be returned by release(). |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head2 Read only! |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
We can't make any changes. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=head1 SEE ALSO |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
L<...> |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Please report problems to Chris Weyl <cweyl@alumni.drew.edu>, or (preferred) |
270
|
|
|
|
|
|
|
to this package's RT tracker at <bug-RPM-Spec@rt.cpan.org>. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
Patches are welcome. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=head1 AUTHOR |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Chris Weyl <cweyl@alumni.drew.edu> |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Copyright (c) 2009 Chris Weyl <cweyl@alumni.drew.edu> |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or |
284
|
|
|
|
|
|
|
modify it under the terms of the GNU Lesser General Public |
285
|
|
|
|
|
|
|
License as published by the Free Software Foundation; either |
286
|
|
|
|
|
|
|
version 2.1 of the License, or (at your option) any later version. |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful, |
289
|
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
290
|
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
291
|
|
|
|
|
|
|
Lesser General Public License for more details. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public |
294
|
|
|
|
|
|
|
License along with this library; if not, write to the |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Free Software Foundation, Inc. |
297
|
|
|
|
|
|
|
59 Temple Place, Suite 330 |
298
|
|
|
|
|
|
|
Boston, MA 02111-1307 USA |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
301
|
|
|
|
|
|
|
|