File Coverage

blib/lib/LCFG/Build/Utils/RPM.pm
Criterion Covered Total %
statement 33 155 21.2
branch 0 60 0.0
condition 0 16 0.0
subroutine 11 16 68.7
pod 2 5 40.0
total 46 252 18.2


line stmt bran cond sub pod time code
1             package LCFG::Build::Utils::RPM; # -*-cperl-*-
2 1     1   782 use strict;
  1         2  
  1         30  
3 1     1   4 use warnings;
  1         1  
  1         33  
4              
5             # $Id: RPM.pm.in 16250 2011-03-03 20:30:10Z squinney@INF.ED.AC.UK $
6             # $Source: /var/cvs/dice/LCFG-Build-Tools/lib/LCFG/Build/Utils/RPM.pm.in,v $
7             # $Revision: 16250 $
8             # $HeadURL: https://svn.lcfg.org/svn/source/tags/LCFG-Build-Tools/LCFG_Build_Tools_0_4_4/lib/LCFG/Build/Utils/RPM.pm.in $
9             # $Date: 2011-03-03 20:30:10 +0000 (Thu, 03 Mar 2011) $
10              
11             our $VERSION = '0.4.4';
12              
13 1     1   791 use DateTime ();
  1         109739  
  1         37  
14 1     1   501 use English qw(-no_match_vars);
  1         1476  
  1         5  
15 1     1   830 use File::Copy ();
  1         1773  
  1         21  
16 1     1   461 use File::Find::Rule ();
  1         7472  
  1         27  
17 1     1   7 use File::Spec ();
  1         1  
  1         11  
18 1     1   3 use File::Temp ();
  1         1  
  1         11  
19 1     1   3 use IO::File ();
  1         1  
  1         9  
20 1     1   508 use Text::Wrap ();
  1         2249  
  1         20  
21              
22 1     1   5 use LCFG::Build::Utils;
  1         1  
  1         1147  
23              
24             sub generate_metadata {
25 0     0 1   my ( $self, $pkgspec, $dir, $outdir ) = @_;
26              
27 0   0       $outdir ||= q{.};
28 0   0       $dir ||= q{.};
29              
30 0           my $specfile = join q{.}, $pkgspec->fullname, 'spec';
31 0           $specfile = File::Spec->catfile( $dir, $specfile );
32              
33 0 0         if ( !-f $specfile ) {
34 0           $specfile = File::Spec->catfile( $dir, 'specfile' );
35 0 0         if ( !-f $specfile ) {
36 0           die "You need to generate a specfile\n";
37             }
38             }
39              
40             # Do our best to find a changelog file of some description.
41              
42 0           my $logfile = $pkgspec->get_vcsinfo('logname');
43 0 0         if ( !defined $logfile ) {
44 0           for my $file (qw/ChangeLog Changes/) {
45 0           my $path = File::Spec->catfile( $dir, $file );
46 0 0         if ( -f $path ) {
47 0           $logfile = $file;
48 0           last;
49             }
50             }
51             }
52              
53 0           my $extra = {};
54 0 0         if ( defined $logfile ) {
55 0           $logfile = File::Spec->catfile( $dir, $logfile );
56 0 0         if ( -f $logfile ) {
57              
58 0           my $changelog = format_changelog($logfile);
59 0           $extra->{LCFG_CHANGELOG} = $changelog;
60             }
61             }
62              
63 0           my $packname = join q{-}, $pkgspec->fullname, $pkgspec->version;
64 0           my $specname = $packname . '.spec';
65 0           my $output = File::Spec->catfile( $outdir, $specname );
66              
67 0           LCFG::Build::Utils::translate_file( $pkgspec, $specfile,
68             $output,
69             $extra );
70              
71             # Do this so the generated tar-file contains a usable specfile
72              
73 0           File::Copy::copy( $output, $specfile );
74              
75 0           return;
76             }
77              
78             sub format_changelog {
79 0     0 0   my ($file) = @_;
80              
81 0           my @entries = parse_changelog($file);
82              
83 0 0         if ( scalar @entries == 0 ) {
84 0           my $dt = DateTime->now();
85 0           my $entry = {
86             year => $dt->year,
87             month => $dt->month,
88             day => $dt->day,
89             };
90 0           return format_entry($entry);
91             }
92              
93 0           my $changelog = q{};
94 0           for my $entry (@entries) {
95 0           $changelog .= format_entry($entry);
96             }
97              
98 0           return $changelog;
99             }
100              
101             sub format_entry {
102 0     0 0   my ($entry) = @_;
103              
104 0           my $dt = eval { DateTime->new( year => $entry->{year},
  0            
105             month => $entry->{month},
106             day => $entry->{day} ) };
107              
108 0 0 0       if ( $EVAL_ERROR || !defined $dt ) {
109 0           return q{};
110             }
111              
112 0           my $formatted_date = $dt->strftime('%a %b %d %Y');
113              
114 0           my $title = $entry->{title};
115 0 0         if ( !defined $title ) {
116 0   0       $title = $ENV{EMAIL} || getpwuid $UID;
117             }
118              
119 0 0 0       if ( $title =~ /\s*cvs:\s*new release/i && defined $entry->{release} ) {
120 0           $title = "<<<< Release: $entry->{release} >>>>";
121             }
122              
123 0           my $output = q{* } . $formatted_date . q{ } . $title . "\n";
124              
125 0           my @body;
126 0 0         if ( defined $entry->{body} ) {
127 0           @body = @{$entry->{body}};
  0            
128             }
129              
130 0 0         if ( scalar @body == 0 ) {
131 0           push @body, 'No release information available';
132             }
133              
134 0           for my $item (@body) {
135 0           $output .= Text::Wrap::wrap( '- ', ' ', $item ) . "\n";
136             }
137              
138 0           $output .= "\n";
139              
140 0           return $output;
141             }
142              
143             sub parse_changelog {
144 0     0 0   my ($file) = @_;
145              
146 0           my @data;
147 0 0 0       if ( !-f $file || -z $file ) {
148 0           return @data;
149             }
150              
151 0 0         my $fh = IO::File->new( $file, 'r' )
152             or die "Could not open file '$file': $OS_ERROR\n";
153              
154 0           my $current;
155 0           while ( defined( my $line = <$fh> ) ) {
156 0           chomp $line;
157              
158 0 0         if ( $line =~ m/^\s*$/ ) {
    0          
159 0           next;
160             } elsif ( $line =~ m/^(\d+)-(\d+)-(\d+)\s*(.*)$/ ) {
161 0           $current = $data[$#data + 1] = { year => $1,
162             month => $2,
163             day => $3,
164             title => $4,
165             body => [] };
166             } else {
167 0           $line =~ s/^\s+//;
168 0           $line =~ s/\s+$//;
169              
170 0           my $body = $current->{body};
171 0 0         if ( $line =~ m/^\*\s*(.+)/ ) {
  0 0          
172 0           my $entry = $1;
173 0 0         if ( $entry =~ m/^release:\s*(.+)$/i ) {
174 0           $current->{release} = $1;
175             }
176              
177 0           push @{$body}, $entry;
  0            
178             } elsif ( scalar @{$body} == 0 ) {
179 0           push @{$body}, $line;
  0            
180             } else {
181 0           ${$body}[$#{$body}] .= " $line";
  0            
  0            
182             }
183             }
184              
185             }
186              
187 0           return @data;
188             }
189              
190             sub build {
191 0     0 1   my ( $self, $dir, $specfile, $options ) = @_;
192              
193 0 0         if ( !defined $options ) {
194 0           $options = {};
195             }
196              
197 0           my @args;
198 0 0         if ( $options->{sourceonly} ) {
199 0           @args = ( '-bs', '--nodeps' );
200             } else {
201 0           @args = ( '-ba' );
202 0 0         if ( $options->{nodeps} ) {
203 0           push @args, '--nodeps';
204             }
205             }
206 0 0         if ( $options->{sign} ) {
207 0           push @args, '--sign';
208             }
209              
210 0           my $tempdir = File::Temp::tempdir( 'buildtools-XXXXX',
211             TMPDIR => 1,
212             CLEANUP => 1 );
213              
214 0           my $builddir;
215 0 0         if ( $options->{builddir} ) {
216 0           $builddir = $options->{builddir};
217             } else {
218 0           $builddir = File::Spec->catdir( $tempdir, 'BUILD' );
219             }
220              
221 0 0         if ( !-d $builddir ) {
222 0 0         mkdir $builddir or die "Could not create directory $builddir: $!\n";
223             }
224              
225 0           my $rpmdir = File::Spec->catdir( $tempdir, 'RPMS' );
226 0 0         mkdir $rpmdir or die "Could not create directory $rpmdir: $!\n";
227              
228 0           my $buildroot = File::Spec->catdir( $tempdir, 'BUILDROOT' );
229              
230 0           my @cmd = ( '/usr/bin/rpmbuild', @args,
231             '--define', "_topdir $dir",
232             '--define', "_builddir $builddir",
233             '--define', "_specdir $dir",
234             '--define', "_sourcedir $dir",
235             '--define', "_srcrpmdir $dir",
236             '--define', "_rpmdir $rpmdir",
237             '--define', "_buildrootdir $buildroot",
238             $specfile );
239              
240 0           my $ok = system @cmd;
241              
242 0 0         if ( $ok != 0 ) {
243 0           die "Failed to build $specfile\n";
244             }
245              
246 0           my ($source) =
247             File::Find::Rule->file()->name('*.src.rpm')->maxdepth(1)->in($dir);
248              
249 0           my @packages;
250 0 0         if ( !$options->{sourceonly} ) {
251 0           my @rpms = File::Find::Rule->file()->name('*.rpm')->in($rpmdir);
252              
253 0           for my $rpm (sort @rpms) {
254 0           my $basename = ( File::Spec->splitpath($rpm) )[2];
255 0           my $target = File::Spec->catfile( $dir, $basename );
256 0 0         File::Copy::move( $rpm, $target )
257             or die "Could not move $rpm to $target: $!\n";
258              
259 0           push @packages, $target;
260             }
261             }
262              
263             return {
264 0           packages => \@packages,
265             source => $source,
266             };
267             }
268              
269             1;
270             __END__
271              
272             =head1 NAME
273              
274             LCFG::Build::Utils::RPM - LCFG software building utilities
275              
276             =head1 VERSION
277              
278             This documentation refers to LCFG::Build::Utils::RPM version 0.4.4
279              
280             =head1 SYNOPSIS
281              
282             my $dir = q{.};
283              
284             my $spec = LCFG::Build::PkgSpec->new_from_metafile("$dir/lcfg.yml");
285              
286             my $resultsdir = '/tmp/foo';
287             LCFG::Build::Utils::RPM->generate_metadata( $spec, $dir, $resultsdir )
288              
289             =head1 DESCRIPTION
290              
291             This module provides a suite of utilities to help in building RPM
292             packages from LCFG projects, particularly LCFG components. The methods
293             are mostly used by tools which implement the LCFG::Build::Tool base
294             class (e.g. LCFG::Build::Tool::RPM) but typically they are designed to
295             be generic enough to be used elsewhere.
296              
297             =head1 SUBROUTINES/METHODS
298              
299             There are two public methods you can call on this class.
300              
301             =over 4
302              
303             =item generate_metadata( $pkgspec, $dir, $outdir )
304              
305             This generates the necessary metadata file (i.e. the specfile) for
306             building RPM packages from this project. It takes an LCFG build
307             package metadata object, an input directory where the template RPM
308             specfile and change log files are stored and an output directory where
309             the generate file should be placed.
310              
311             =item build( $dir, $specfile, $options )
312              
313             This actually builds the RPM packages using the C<rpmbuild>
314             command. It requires the name of the directory which contains the
315             source tar file and the RPM specfile. A reference to a hash of options
316             can be passed in, this allows one to specify things like only building
317             the source package (with "sourceonly") and making rpmbuild ignore
318             dependencies with "nodeps".
319              
320             =back
321              
322             =head1 DEPENDENCIES
323              
324             For formatting the change log file you will need DateTime(3).
325              
326             =head1 PLATFORMS
327              
328             This is the list of platforms on which we have tested this
329             software. We expect this software to work on any Unix-like platform
330             which is supported by Perl.
331              
332             Fedora12, Fedora13, ScientificLinux5, ScientificLinux6, MacOSX7
333              
334             =head1 BUGS AND LIMITATIONS
335              
336             There are no known bugs in this application. Please report any
337             problems to bugs@lcfg.org, feedback and patches are also always very
338             welcome.
339              
340             =head1 AUTHOR
341              
342             Stephen Quinney <squinney@inf.ed.ac.uk>
343              
344             =head1 LICENSE AND COPYRIGHT
345              
346             Copyright (C) 2008 University of Edinburgh. All rights reserved.
347              
348             This library is free software; you can redistribute it and/or modify
349             it under the terms of the GPL, version 2 or later.
350              
351             =cut