source: ProjectBuilder/0.9.1/pb/lib/ProjectBuilder/CMS.pm@ 414

Last change on this file since 414 was 414, checked in by Bruno Cornec, 16 years ago

Remove executables rights for pm modules

File size: 17.1 KB
Line 
1#!/usr/bin/perl -w
2#
3# Project Builder CMS module
4# CMS subroutines brought by the the Project-Builder project
5# which can be easily used by pbinit scripts
6#
7# $Id$
8#
9# Copyright B. Cornec 2007
10# Provided under the GPL v2
11
12package ProjectBuilder::CMS;
13
14use strict 'vars';
15use Data::Dumper;
16use English;
17use File::Basename;
18use POSIX qw(strftime);
19use lib qw (lib);
20use ProjectBuilder::Base;
21use ProjectBuilder::Conf;
22
23# Inherit from the "Exporter" module which handles exporting functions.
24
25use Exporter;
26
27# Export, by default, all the functions into the namespace of
28# any code which uses this module.
29
30our @ISA = qw(Exporter);
31our @EXPORT = qw(pb_cms_init pb_cms_export pb_cms_get_uri pb_cms_copy pb_cms_checkout pb_cms_up pb_cms_checkin pb_cms_isdiff pb_cms_get_pkg pb_cms_compliant pb_cms_log);
32
33=pod
34
35=head1 NAME
36
37ProjectBuilder::CMS, part of the project-builder.org
38
39=head1 DESCRIPTION
40
41This modules provides configuration management system functions suitable for pbinit calls.
42
43=head1 USAGE
44
45=over 4
46
47=item B<pb_cms_init>
48
49This function setup the environment for the CMS system related to the URL given by the pburl configuration parameter.
50The potential parameter indicates whether we should inititate the context or not.
51It sets up environement variables (PBPROJDIR, PBDIR, PBREVISION, PBCMSLOGFILE)
52
53=cut
54
55sub pb_cms_init {
56
57my $pbinit = shift || undef;
58
59my ($pburl) = pb_conf_get("pburl");
60pb_log(2,"DEBUG: Project URL of $ENV{'PBPROJ'}: $pburl->{$ENV{'PBPROJ'}}\n");
61my ($scheme, $account, $host, $port, $path) = pb_get_uri($pburl->{$ENV{'PBPROJ'}});
62
63my ($pbprojdir) = pb_conf_get_if("pbprojdir");
64
65if ((defined $pbprojdir) && (defined $pbprojdir->{$ENV{'PBPROJ'}})) {
66 $ENV{'PBPROJDIR'} = $pbprojdir->{$ENV{'PBPROJ'}};
67} else {
68 $ENV{'PBPROJDIR'} = "$ENV{'PBDEFDIR'}/$ENV{'PBPROJ'}";
69}
70
71# Computing the default dir for PBDIR.
72# what we have is PBPROJDIR so work from that.
73# Tree identical between PBCONFDIR and PBROOTDIR on one side and
74# PBPROJDIR and PBDIR on the other side.
75
76my $tmp = $ENV{'PBROOTDIR'};
77$tmp =~ s|^$ENV{'PBCONFDIR'}||;
78
79#
80# Check project cms compliance
81#
82pb_cms_compliant(undef,'PBDIR',"$ENV{'PBPROJDIR'}/$tmp",$pburl->{$ENV{'PBPROJ'}},$pbinit);
83
84if ($scheme =~ /^svn/) {
85 # svnversion more precise than svn info
86 $tmp = `(cd "$ENV{'PBDIR'}" ; svnversion .)`;
87 chomp($tmp);
88 $ENV{'PBREVISION'}=$tmp;
89 $ENV{'PBCMSLOGFILE'}="svn.log";
90} elsif (($scheme eq "file") || ($scheme eq "ftp") || ($scheme eq "http")) {
91 $ENV{'PBREVISION'}="flat";
92 $ENV{'PBCMSLOGFILE'}="flat.log";
93} elsif ($scheme =~ /^cvs/) {
94 # Way too slow
95 #$ENV{'PBREVISION'}=`(cd "$ENV{'PBROOTDIR'}" ; cvs rannotate -f . 2>&1 | awk '{print \$1}' | grep -E '^[0-9]' | cut -d. -f2 |sort -nu | tail -1)`;
96 #chomp($ENV{'PBREVISION'});
97 $ENV{'PBREVISION'}="cvs";
98 $ENV{'PBCMSLOGFILE'}="cvs.log";
99 $ENV{'CVS_RSH'} = "ssh" if ($scheme =~ /ssh/);
100} else {
101 die "cms $scheme unknown";
102}
103
104return($scheme,$pburl->{$ENV{'PBPROJ'}});
105}
106
107=item B<pb_cms_export>
108
109This function exports a CMS content to a directory.
110The first parameter is the URL of the CMS content.
111The second parameter is the directory in which it is locally exposed (result of a checkout).
112The third parameter is the directory where we want to deliver it (result of export).
113
114=cut
115
116sub pb_cms_export {
117
118my $uri = shift;
119my $source = shift;
120my $destdir = shift;
121my $tmp;
122my $tmp1;
123
124my @date = pb_get_date();
125# If it's not flat, then we have a real uri as source
126my ($scheme, $account, $host, $port, $path) = pb_get_uri($uri);
127
128if ($scheme =~ /^svn/) {
129 if (-d $source) {
130 $tmp = $destdir;
131 } else {
132 $tmp = "$destdir/".basename($source);
133 }
134 pb_system("svn export $source $tmp","Exporting $source from SVN to $tmp");
135} elsif ($scheme eq "dir") {
136 pb_system("cp -a $path $destdir","Copying $uri from DIR to $destdir");
137} elsif (($scheme eq "http") || ($scheme eq "ftp")) {
138 my $f = basename($path);
139 unlink "$ENV{'PBTMP'}/$f";
140 if (-x "/usr/bin/wget") {
141 pb_system("/usr/bin/wget -nv -O $ENV{'PBTMP'}/$f $uri"," ");
142 } elsif (-x "/usr/bin/curl") {
143 pb_system("/usr/bin/curl $uri -o $ENV{'PBTMP'}/$f","Downloading $uri with curl to $ENV{'PBTMP'}/$f\n");
144 } else {
145 die "Unable to download $uri.\nNo wget/curl available, please install one of those";
146 }
147 pb_cms_export("file://$ENV{'PBTMP'}/$f",$source,$destdir);
148} elsif ($scheme eq "file") {
149 use File::MimeInfo;
150 my $mm = mimetype($path);
151 pb_log(2,"mimetype: $mm\n");
152 pb_mkdir_p($destdir);
153
154 # Check whether the file is well formed
155 # (containing already a directory with the project-version name)
156 my ($pbwf) = pb_conf_get_if("pbwf");
157 if ((defined $pbwf) && (defined $pbwf->{$ENV{'PBPROJ'}})) {
158 $destdir = dirname($destdir);
159 }
160
161 if ($mm =~ /\/x-bzip-compressed-tar$/) {
162 # tar+bzip2
163 pb_system("cd $destdir ; tar xfj $path","Extracting $path in $destdir");
164 } elsif ($mm =~ /\/x-lzma-compressed-tar$/) {
165 # tar+lzma
166 pb_system("cd $destdir ; tar xfY $path","Extracting $path in $destdir");
167 } elsif ($mm =~ /\/x-compressed-tar$/) {
168 # tar+gzip
169 pb_system("cd $destdir ; tar xfz $path","Extracting $path in $destdir");
170 } elsif ($mm =~ /\/x-tar$/) {
171 # tar
172 pb_system("cd $destdir ; tar xf $path","Extracting $path in $destdir");
173 } elsif ($mm =~ /\/zip$/) {
174 # zip
175 pb_system("cd $destdir ; unzip $path","Extracting $path in $destdir");
176 }
177} elsif ($scheme =~ /^cvs/) {
178 # CVS needs a relative path !
179 my $dir=dirname($destdir);
180 my $base=basename($destdir);
181 # CVS also needs a modules name not a dir
182 #if (-d $source) {
183 $tmp1 = basename($source);
184 #} else {
185 #$tmp1 = dirname($source);
186 #$tmp1 = basename($tmp1);
187 #}
188 my $optcvs = "";
189
190 # If we're working on the CVS itself
191 my $cvstag = basename($ENV{'PBROOTDIR'});
192 my $cvsopt = "";
193 if ($cvstag eq "cvs") {
194 my $pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
195 $cvsopt = "-D \"$pbdate\"";
196 } else {
197 # we're working on a tag which should be the last part of PBROOTDIR
198 $cvsopt = "-r $cvstag";
199 }
200 pb_system("cd $dir ; cvs -d $account\@$host:$path export $cvsopt -d $base $tmp1","Exporting $tmp1 from $source under CVS to $destdir");
201} else {
202 die "cms $scheme unknown";
203}
204}
205
206=item B<pb_cms_get_uri>
207
208This function is only called with a real CMS system and gives the URL stored in the checked out directory.
209The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
210The second parameter is the directory in which it is locally exposed (result of a checkout).
211
212=cut
213
214sub pb_cms_get_uri {
215
216my $scheme = shift;
217my $dir = shift;
218
219my $res = "";
220my $void = "";
221
222if ($scheme =~ /^svn/) {
223 open(PIPE,"LANGUAGE=C svn info $dir |") || return("");
224 while (<PIPE>) {
225 ($void,$res) = split(/^URL:/) if (/^URL:/);
226 }
227 $res =~ s/^\s*//;
228 close(PIPE);
229 chomp($res);
230} elsif ($scheme =~ /^cvs/) {
231 # This path is always the root path of CVS, but we may be below
232 open(FILE,"$dir/CVS/Root") || die "$dir isn't CVS controlled";
233 $res = <FILE>;
234 chomp($res);
235 close(FILE);
236 # Find where we are in the tree
237 my $rdir = $dir;
238 while ((! -d "$rdir/CVSROOT") && ($rdir ne "/")) {
239 $rdir = dirname($rdir);
240 }
241 die "Unable to find a CVSROOT dir in the parents of $dir" if (! -d "$rdir/CVSROOT");
242 #compute our place under that root dir - should be a relative path
243 $dir =~ s|^$rdir||;
244 my $suffix = "";
245 $suffix = "$dir" if ($dir ne "");
246
247 my $prefix = "";
248 if ($scheme =~ /ssh/) {
249 $prefix = "cvs+ssh://";
250 } else {
251 $prefix = "cvs://";
252 }
253 $res = $prefix.$res.$suffix;
254} else {
255 die "cms $scheme unknown";
256}
257pb_log(2,"Found CMS info: $res\n");
258return($res);
259}
260
261=item B<pb_cms_copy>
262
263This function copies a CMS content to another.
264The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
265The second parameter is the URL of the original CMS content.
266The third parameter is the URL of the destination CMS content.
267
268Only coded for SVN now.
269
270=cut
271
272sub pb_cms_copy {
273my $scheme = shift;
274my $oldurl = shift;
275my $newurl = shift;
276
277if ($scheme =~ /^svn/) {
278 pb_system("svn copy -m \"Creation of $newurl from $oldurl\" $oldurl $newurl","Copying $oldurl to $newurl ");
279} elsif ($scheme eq "flat") {
280} elsif ($scheme =~ /^cvs/) {
281} else {
282 die "cms $scheme unknown";
283}
284}
285
286=item B<pb_cms_checkout>
287
288This function checks a CMS content out to a directory.
289The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
290The second parameter is the URL of the CMS content.
291The third parameter is the directory where we want to deliver it (result of export).
292
293=cut
294
295sub pb_cms_checkout {
296my $scheme = shift;
297my $url = shift;
298my $destination = shift;
299
300if ($scheme =~ /^svn/) {
301 pb_system("svn co $url $destination","Checking out $url to $destination ");
302} elsif (($scheme eq "ftp") || ($scheme eq "http")) {
303 return;
304} elsif ($scheme =~ /^cvs/) {
305 pb_system("cvs co $url $destination","Checking out $url to $destination ");
306} else {
307 die "cms $scheme unknown";
308}
309}
310
311=item B<pb_cms_up>
312
313This function updates a local directory with the CMS content.
314The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
315The second parameter is the directory to update.
316
317=cut
318
319sub pb_cms_up {
320my $scheme = shift;
321my $dir = shift;
322
323if ($scheme =~ /^svn/) {
324 pb_system("svn up $dir","Updating $dir");
325} elsif ($scheme eq "flat") {
326} elsif ($scheme =~ /^cvs/) {
327 pb_system("cvs up $dir","Updating $dir");
328} else {
329 die "cms $scheme unknown";
330}
331}
332
333=item B<pb_cms_checkin>
334
335This function updates a CMS content from a local directory.
336The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
337The second parameter is the directory to update from.
338
339=cut
340
341sub pb_cms_checkin {
342my $scheme = shift;
343my $dir = shift;
344
345my $ver = basename($dir);
346if ($scheme =~ /^svn/) {
347 pb_system("svn ci -m \"updated to $ver\" $dir","Checking in $dir");
348} elsif ($scheme eq "flat") {
349} elsif ($scheme =~ /^cvs/) {
350 pb_system("cvs ci -m \"updated to $ver\" $dir","Checking in $dir");
351} else {
352 die "cms $scheme unknown";
353}
354pb_cms_up($scheme,$dir);
355}
356
357=item B<pb_cms_isdiff>
358
359This function returns a integer indicating the number f differences between the CMS content and the local directory where it's checked out.
360The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
361The second parameter is the directory to consider.
362
363=cut
364
365sub pb_cms_isdiff {
366my $scheme = shift;
367my $dir =shift;
368
369if ($scheme =~ /^svn/) {
370 open(PIPE,"svn diff $dir |") || die "Unable to get svn diff from $dir";
371 my $l = 0;
372 while (<PIPE>) {
373 $l++;
374 }
375 return($l);
376} elsif ($scheme eq "flat") {
377} elsif ($scheme =~ /^cvs/) {
378 open(PIPE,"cvs diff $dir |") || die "Unable to get svn diff from $dir";
379 my $l = 0;
380 while (<PIPE>) {
381 # Skipping normal messages
382 next if (/^cvs diff:/);
383 $l++;
384 }
385 return($l);
386} else {
387 die "cms $scheme unknown";
388}
389}
390
391=item B<pb_cms_isdiff>
392
393This function returns the list of packages we are working on in a CMS action.
394The first parameter is the default list of packages from the configuration file.
395The second parameter is the optional list of packages from the configuration file.
396
397=cut
398
399sub pb_cms_get_pkg {
400
401my @pkgs = ();
402my $defpkgdir = shift || undef;
403my $extpkgdir = shift || undef;
404
405# Get packages list
406if (not defined $ARGV[0]) {
407 @pkgs = keys %$defpkgdir if (defined $defpkgdir);
408} elsif ($ARGV[0] =~ /^all$/) {
409 @pkgs = keys %$defpkgdir if (defined $defpkgdir);
410 push(@pkgs, keys %$extpkgdir) if (defined $extpkgdir);
411} else {
412 @pkgs = @ARGV;
413}
414pb_log(0,"Packages: ".join(',',@pkgs)."\n");
415return(\@pkgs);
416}
417
418=item B<pb_cms_compliant>
419
420This function checks the compliance of the project and the pbconf directory.
421The first parameter is the key name of the value that needs to be read in the configuration file.
422The second parameter is the environment variable this key will populate.
423The third parameter is the location of the pbconf dir.
424The fourth parameter is the URI of the CMS content related to the pbconf dir.
425The fifth parameter indicates whether we should inititate the context or not.
426
427=cut
428
429sub pb_cms_compliant {
430
431my $param = shift;
432my $envar = shift;
433my $defdir = shift;
434my $uri = shift;
435my $pbinit = shift;
436my %pdir;
437
438my ($pdir) = pb_conf_get_if($param) if (defined $param);
439if (defined $pdir) {
440 %pdir = %$pdir;
441}
442
443
444if ((defined $pdir) && (%pdir) && (defined $pdir{$ENV{'PBPROJ'}})) {
445 # That's always the environment variable that will be used
446 $ENV{$envar} = $pdir{$ENV{'PBPROJ'}};
447} else {
448 if (defined $param) {
449 pb_log(1,"WARNING: no $param defined, using $defdir\n");
450 pb_log(1," Please create a $param reference for project $ENV{'PBPROJ'} in $ENV{'PBETC'}\n");
451 pb_log(1," if you want to use another directory\n");
452 }
453 $ENV{$envar} = "$defdir";
454}
455
456# Expand potential env variable in it
457eval { $ENV{$envar} =~ s/(\$ENV.+\})/$1/eeg };
458pb_log(2,"$envar: $ENV{$envar}\n");
459
460my ($scheme, $account, $host, $port, $path) = pb_get_uri($uri);
461
462if ((! -d "$ENV{$envar}") || (defined $pbinit)) {
463 if (defined $pbinit) {
464 pb_mkdir_p("$ENV{$envar}");
465 } else {
466 pb_log(1,"Checking out $uri\n");
467 pb_cms_checkout($scheme,$uri,$ENV{$envar});
468 }
469} elsif (($scheme !~ /^cvs/) || ($scheme !~ /^svn/)) {
470 # Do not compare if it's not a real cms
471 return;
472} else {
473 pb_log(1,"$uri found locally, checking content\n");
474 my $cmsurl = pb_cms_get_uri($scheme,$ENV{$envar});
475 my ($scheme2, $account2, $host2, $port2, $path2) = pb_get_uri($cmsurl);
476 if ($cmsurl ne $uri) {
477 # The local content doesn't correpond to the repository
478 pb_log(0,"ERROR: Inconsistency detected:\n");
479 pb_log(0," * $ENV{$envar} refers to $cmsurl but\n");
480 pb_log(0," * $ENV{'PBETC'} refers to $uri\n");
481 die "Project $ENV{'PBPROJ'} is not Project-Builder compliant.";
482 } else {
483 pb_log(1,"Content correct - doing nothing - you may want to update your repository however\n");
484 # they match - do nothing - there may be local changes
485 }
486}
487}
488
489=item B<pb_cms_create_authors>
490
491This function creates a AUTHORS files for the project. It call it AUTHORS.pb if an AUTHORS file already exists.
492The first parameter is the source file for authors information.
493The second parameter is the directory where to create the final AUTHORS file.
494The third parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
495
496=cut
497
498sub pb_cms_create_authors {
499
500my $authors=shift;
501my $dest=shift;
502my $scheme=shift;
503
504return if ($authors eq "/dev/null");
505open(SAUTH,$authors) || die "Unable to open $authors";
506# Save a potentially existing AUTHORS file and write instead to AUTHORS.pb
507my $ext = "";
508if (-f "$dest/AUTHORS") {
509 $ext = ".pb";
510}
511open(DAUTH,"> $dest/AUTHORS$ext") || die "Unable to create $dest/AUTHORS$ext";
512print DAUTH "Authors of the project are:\n";
513print DAUTH "===========================\n";
514while (<SAUTH>) {
515 my ($nick,$gcos) = split(/:/);
516 chomp($gcos);
517 print DAUTH "$gcos";
518 if (defined $scheme) {
519 # Do not give a scheme for flat types
520 my $endstr="";
521 if ("$ENV{'PBREVISION'}" ne "flat") {
522 $endstr = " under $scheme";
523 }
524 print DAUTH " ($nick$endstr)\n";
525 } else {
526 print DAUTH "\n";
527 }
528}
529close(DAUTH);
530close(SAUTH);
531}
532
533=item B<pb_cms_log>
534
535This function creates a ChangeLog file for the project.
536The first parameter is the schema of the CMS systems (svn, cvs, svn+ssh, ...)
537The second parameter is the directory where the CMS content was checked out.
538The third parameter is the directory where to create the final ChangeLog file.
539The fourth parameter is unused.
540The fifth parameter is the source file for authors information.
541
542It may use a tool like svn2cl or cvs2cl to generate it if present, or the log file from the CMS if not.
543
544=cut
545
546
547sub pb_cms_log {
548
549my $scheme = shift;
550my $pkgdir = shift;
551my $dest = shift;
552my $chglog = shift;
553my $authors = shift;
554
555pb_cms_create_authors($authors,$dest,$scheme);
556
557if ($scheme =~ /^svn/) {
558 if (! -f "$dest/ChangeLog") {
559 if (-x "/usr/bin/svn2cl") {
560 # In case we have no network, just create an empty one before to allow correct build
561 open(CL,"> $dest/ChangeLog") || die "Unable to create $dest/ChangeLog";
562 close(CL);
563 pb_system("/usr/bin/svn2cl --group-by-day --authors=$authors -i -o $dest/ChangeLog $pkgdir","Generating ChangeLog from SVN with svn2cl");
564 } else {
565 # To be written from pbcl
566 pb_system("svn log -v $pkgdir > $dest/$ENV{'PBCMSLOGFILE'}","Extracting log info from SVN");
567 }
568 }
569} elsif (($scheme eq "file") || ($scheme eq "dir") || ($scheme eq "http") || ($scheme eq "ftp")) {
570 if (! -f "$dest/ChangeLog") {
571 pb_system("echo ChangeLog for $pkgdir > $dest/ChangeLog","Empty ChangeLog file created");
572 }
573} elsif ($scheme =~ /^cvs/) {
574 my $tmp=basename($pkgdir);
575 # CVS needs a relative path !
576 if (! -f "$dest/ChangeLog") {
577 if (-x "/usr/bin/cvs2cl") {
578 # In case we have no network, just create an empty one before to allow correct build
579 open(CL,"> $dest/ChangeLog") || die "Unable to create $dest/ChangeLog";
580 close(CL);
581 pb_system("/usr/bin/cvs2cl --group-by-day -U $authors -f $dest/ChangeLog $pkgdir","Generating ChangeLog from CVS with cvs2cl");
582 } else {
583 # To be written from pbcl
584 pb_system("cvs log $tmp > $dest/$ENV{'PBCMSLOGFILE'}","Extracting log info from CVS");
585 }
586 }
587} else {
588 die "cms $scheme unknown";
589}
590}
591
592=back
593
594=head1 WEB SITES
595
596The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
597
598=head1 USER MAILING LIST
599
600None exists for the moment.
601
602=head1 AUTHORS
603
604The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
605
606=head1 COPYRIGHT
607
608Project-Builder.org is distributed under the GPL v2.0 license
609described in the file C<COPYING> included with the distribution.
610
611=cut
612
6131;
Note: See TracBrowser for help on using the repository browser.