import rt 2.0.14
[freeside.git] / rt / tools / cpan2rpm
1 #!/usr/bin/perl -w
2
3
4 # Take a perl tarball and make a specfile for it. Now with bundles.
5 #
6 # Copyright 2000,2001 Simon Wilkinson. All rights reserved.
7 #
8 # 10/18/2001 - <jesse@bestpractical.com>
9 #               Added resolution of prereq_pm modules
10 #
11 # This program is free software; you can redistribute it 
12 # and/or modify it under the same terms as Perl itself.
13 #
14
15 use strict;
16
17 use CPAN;
18 use POSIX;
19 use Sys::Hostname;
20 use File::Basename;
21 use Getopt::Long;
22
23 use vars qw ($DEBUG $ARCH $builddir $seen @report);
24
25 $ARCH = 'i386';
26
27 $DEBUG = 0;
28 # Icky globals
29
30 my $release;
31 my $package;
32
33 $seen = {};
34
35
36 sub usage 
37 {
38   print STDERR <<EOM;
39
40 Usage: cpan2rpm [--release <release>] [--builddir <rpm build dir>]  <Perl::Module>
41
42 Where:
43 <release> is the release number of the RPM you wish to produce
44 <Perl::Module> is the name of the module to build
45 EOM
46   exit(1);
47 }
48
49
50 my $ret=GetOptions("release" => \$release,
51                    "builddir=s" => \$builddir);
52
53 $package=$ARGV[0];
54 usage() if !$package;
55 $release=1 if !$release;
56
57
58 $builddir=ExtractRpmMacro($ENV{HOME}."/.rpmmacros","_topdir") if !$builddir;
59 $builddir=ExtractRpmMacro("/etc/rpm/macros","_topdir") if !$builddir;
60 $builddir=getcwd() if !$builddir;
61   
62 die "Build directory $builddir doesn't look like an RPM build root\n"
63     if ((! -d "$builddir/SPECS") || (! -d "$builddir/SOURCES"));
64
65 process($package,$release);
66
67 print join("\n",@report)."\n";
68
69 sub process {
70   my ($infile,$release)=@_;
71
72   
73
74 # Convert our installation list into an unbundled one
75   unbundle($infile);
76
77   print "Building $infile\n";
78
79     cpan2rpm($infile,$builddir,$release);
80
81 }
82
83 # Given a Module, try to split it into its required components - this
84 # currently only handles Bundles, but could also be extended to deal with
85 # prereqs as well.
86
87 sub unbundle {
88   my ($item) = @_;
89
90   if ($item=~/Bundle::/) {
91     my $obj=CPAN::Shell->expand('Bundle',$item);
92
93     foreach my $kid ($obj->contains) {
94         process($kid,$release);
95     }
96   }
97 }
98
99
100 sub cpan2rpm($$$) {
101   my ($infile,$builddir,$release) = @_;
102
103   my $ret;
104
105   my $obj=CPAN::Shell->expand('Module',$infile);
106
107   print "CPAN tells us the following about $infile:\n",$obj->as_string if ($DEBUG);
108
109   $ret=fetch_source($obj,$builddir);
110   $ret=build_specfile($obj,$builddir,$release) if !$ret;
111   
112   return $ret;
113 }
114
115 # FIXME: Some error handling in the function below wouldn't go amiss ...
116 sub fetch_source {
117   my ($obj,$builddir)=@_;
118
119   # Minor Sanity checks
120   my $id=$obj->{ID};
121
122   return "Error: No file for $id\n" 
123      if $obj->cpan_file eq "N/A";
124   return "Error: $id says 'Contact Author'\n" 
125      if $obj->cpan_file =~ /^Contact Author/;
126   return "Error: $id is contained within Perl itself!\n"
127      if ($obj->cpan_file =~/perl-5\.\d?\.\d?\.tar\.gz/xo);
128
129   # We do this so we can take advantage of CPAN's object caching. This is
130   # pinched from the CPAN::Distribution::get method, which we can't use
131   # directly, as it untars the package as well - which we let RPM do.
132  
133   my $dist = $CPAN::META->instance('CPAN::Distribution',$obj->cpan_file);
134
135
136   my($local_wanted) =
137        MM->catfile($CPAN::Config->{keep_source_where},
138                    "authors",
139                    "id",
140                    split("/",$dist->{ID})
141                    );
142
143   my $local_file = CPAN::FTP->localize("authors/id/$dist->{ID}", $local_wanted);
144   
145   $dist->{localfile} = $local_file;
146
147   $dist->verifyMD5 if ($CPAN::META->has_inst('MD5'));
148
149
150   # Find all the prereqs for this distribution, then build em.
151   # TODO this should be somewhere else
152
153   $dist->make;
154   build_prereqs( $dist->prereq_pm());
155
156
157
158   my $infile=basename($obj->cpan_file);
159
160   File::Copy::copy($local_file,"$builddir/SOURCES/$infile");
161
162   return undef;
163 }
164
165
166 sub build_prereqs($) {
167   my ($prereqs) = @_;
168   
169   foreach my $prereq (keys %{$prereqs}) {
170         process ($prereq, $release);
171   }
172 }
173 sub build_specfile($$$) {
174   my ($obj,$builddir,$release) = @_;
175
176   my $source=basename($obj->cpan_file);
177
178   # don't go through dependencies on something we've already dealt with
179   return() if ($seen->{$source});
180   $seen->{$source} = 1; 
181
182 my ($name,$version)=($source=~/(.*)-(.*)\.tar\.gz/);
183   return "Couldn't get a name for $source\n" if !$name;
184   return "Couldn't get a version for $source\n" if !$version; 
185   
186   my $summary="$name module for perl";
187   my $description=$obj->{description};
188   $description= $summary if !$description;
189
190   open(SPEC, ">$builddir/SPECS/perl-$name.spec")
191     or die "Couldn't open perl-$name.spec for writing.";
192   print SPEC <<EOF;
193
194 Summary: $summary
195 Name: perl-$name
196 Version: $version
197 Release: $release
198 Copyright: distributable
199 Group: Applications/CPAN
200 Source0: $source
201 Url: http://www.cpan.org
202 BuildRoot: /var/tmp/perl-${name}-buildroot/
203 Requires: perl 
204
205 %description
206 This is a perl module, autogenerated by cpan2rpm. The original package's
207 description was:
208
209 $description
210
211 %prep
212 %setup -q -n $name-%{version}
213
214 %build
215 CFLAGS="\$RPM_OPT_FLAGS" perl Makefile.PL
216 make
217
218 %clean
219 rm -rf \$RPM_BUILD_ROOT
220
221 %install
222 rm -rf \$RPM_BUILD_ROOT
223 eval `perl '-V:installarchlib'`
224 mkdir -p \$RPM_BUILD_ROOT/\$installarchlib
225 make PREFIX=\$RPM_BUILD_ROOT/usr install
226 /usr/lib/rpm/brp-compress
227 find \$RPM_BUILD_ROOT/usr -type f -print | sed "s\@^\$RPM_BUILD_ROOT\@\@g" | grep -v perllocal.pod > $name-$version-filelist
228
229 %files -f ${name}-${version}-filelist
230 %defattr(-,root,root)
231
232 %changelog
233 EOF
234   print SPEC "* ",POSIX::strftime("%a %b %d %Y",localtime()), " ",$ENV{USER}," <",$ENV{USER},"\@",hostname(),">\n";
235   print SPEC "- Spec file automatically generated by cpan2rpm\n";
236
237   close(SPEC);
238
239   system("rpm -ba $builddir/SPECS/perl-$name.spec >/dev/null") == 0
240     or push (@report,  "RPM of $source failed with : $!\n"); 
241  
242   system("rpm -Uvh $builddir/RPMS/$ARCH/perl-$name-$version-$release.$ARCH.rpm") == 0
243     or warn "RPM of $source could not be installed: $!\n";
244
245   push (@report,  "Built perl-$name-$version-$release.$ARCH.rpm");
246 }
247
248 sub ExtractRpmMacro {
249   my ($file,$macro) = @_;
250
251   my $handle=new IO::File;
252
253   if (!$handle->open($file)) {
254     return undef;
255   }
256
257   while(<$handle>) {
258     if (/\%$macro (.*)/) {
259        return $1;
260     }
261   }
262
263   return undef;
264 }
265
266 =head1 NAME
267
268 cpan2rpm - fetch and convert CPAN packages to RPMs
269
270 =head1 SYNOPSIS
271
272 cpan2rpm --release <release> <package>
273
274 =head1 DESCRIPTION
275
276 cpan2rpm provides a quick way of creating RPM packages from perl modules
277 published on CPAN. It interfaces with the perl CPAN module to fetch the
278 file from the selected mirror, and then creates a spec file from the 
279 information in CPAN, and invokes RPM on that spec file.
280
281 Files are created in the users RPM build root.
282
283 =head1 OPTIONS
284
285 =over 4
286
287 =item release
288
289 Sets the release number of the created RPMs.
290
291 =back
292
293 =head1 SEE ALSO
294
295 rpm(1)
296
297 =head1 AUTHOR
298
299 Simon Wilkinson <sxw@sxw.org.uk>