3 # Copyright 2001, 2002, 2003 Steven Knight. All rights reserved. This program
4 # is free software; you can redistribute and/or modify under the
5 # same terms as Perl itself.
8 $revision = "src/ae2cvs.pl 0.D002 2001/10/03 09:36:49 software";
15 use vars qw( @add_list @args @cleanup @copy_list @libraries
16 @mkdir_list @remove_list
18 $ae_copy $aedir $aedist $cnum $commit $common $cvsmod
19 $delta $description $exec $help $indent $infile
21 $summary $usedir $usepath );
30 Getopt::Long::Configure('no_ignore_case');
32 my $ret = GetOptions (
33 "aedist" => sub { $aedist = 1 },
34 "aegis" => sub { $aedist = 0 },
38 "library=s" => \@libraries,
39 "module=s" => \$cvsmod,
40 "noexecute" => sub { $exec = 0 },
41 "project=s" => \$proj,
43 "usedir=s" => \$usedir,
44 "x|execute" => sub { $exec++ if ! defined $exec || $exec != 0 },
45 "X|EXECUTE" => sub { $exec = 2 if ! defined $exec || $exec != 0 },
48 Pod::Usage::pod2usage(-verbose => 0) if $help || ! $ret;
50 $exec = 0 if ! defined $exec;
54 # Wrap up the $quiet logic in one place.
58 my $string = join('', @_);
59 $string =~ s/^/$indent/msg if $indent;
64 # Wrappers for executing various builtin Perl functions in
65 # accordance with the -n, -q and -x options.
77 my ($source, $dest) = @_;
78 printit "cp $source $dest\n";
89 chdir($dir) || die "ae2cvs: could not chdir($dir): $!";
95 printit "mkdir $dir\n";
102 # Put some input data through an external filter and capture the output.
105 my ($cmd, $input) = @_;
110 my $pid = open2(*READ, *WRITE, $cmd) || die "Cannot exec '$cmd': $!\n";
113 my $output = join('', <READ>);
124 # Fetch the file list either from our aedist input
125 # or directly from the project itself.
131 my $infile_redir = "";
133 if (! $infile || $infile eq "-") {
134 $contents = join('', <STDIN>);
136 open(FILE, "<$infile") || die "Cannot open '$infile': $!\n";
138 $contents = join('', <FILE>);
140 if (! File::Spec->file_name_is_absolute($infile)) {
141 $infile = File::Spec->catfile($pwd, $infile);
143 $infile_redir = " < $infile";
146 my $output = filter("aedist -l -unf", $contents);
149 if (! defined $proj) {
150 ($proj = $output) =~ s/PROJECT\n([^\n]*)\n.*/$1/ms;
152 ($summary = $output) =~ s/.*\nSUMMARY\n([^\n]*)\n.*/$1/ms;
153 ($description = $output) =~ s/.*\nDESCRIPTION\n([^\n]*)\nCAUSE\n.*/$1/ms;
154 ($filesection = $output) =~ s/.*\nFILES\n//ms;
155 @filelines = split(/\n/, $filesection);
158 printit qq(MYTMP="/tmp/ae2cvs-ae.\$\$"\n),
161 printit q(perl -MMIME::Base64 -e 'undef $/; ($c = <>) =~ s/.*\n\n//ms; print decode_base64($c)'),
164 qq( | cpio -i -d --quiet\n);
166 push(@cleanup, $aedir);
168 $aedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs-ae.$$");
170 push(@cleanup, $aedir);
175 $contents =~ s/.*\n\n//ms;
176 $contents = filter("zcat", decode_base64($contents));
178 open(CPIO, "|cpio -i -d --quiet");
179 print CPIO $contents;
185 my $source = File::Spec->catfile($aedir, "src", $dest);
186 execute(qq(cp $source $dest));
189 $cnum = $ENV{AEGIS_CHANGE} if ! defined $cnum;
190 $proj = $ENV{AEGIS_PROJECT} if ! defined $proj;
192 $common = "-lib " . join(" -lib ", @libraries) if @libraries;
193 $common = "$common -proj $proj" if $proj;
195 foreach (`aegis -l ph -unf $common`) {
197 if (/^(\d+) .{24} $cnum\s*(.*)/) {
204 print STDERR "ae2cvs: No change $cnum for project $proj.\n";
208 @filelines = `aegis -l cf -unf -c $cnum $common`;
212 execute(qq(aegis -cp -ind -delta $delta $common $file));
217 $usedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs.$$");
219 push(@cleanup, $usedir);
225 if (! File::Spec->file_name_is_absolute($usepath)) {
226 $usepath = File::Spec->catfile($pwd, $usepath);
229 if (! -d File::Spec->catfile($usedir, "CVS")) {
230 $cvsmod = (split(/\./, $proj))[0] if ! defined $cvsmod;
232 execute(qq(cvs -Q co $cvsmod));
236 $usepath = File::Spec->catfile($usepath, $cvsmod);
240 # Figure out what we have to do to accomplish everything.
242 foreach (@filelines) {
243 my @arr = split(/\s+/, $_);
244 my $type = shift @arr; # source / test
245 my $act = shift @arr; # modify / create
248 if ($act eq "create" or $act eq "modify") {
249 # XXX Do we really only need to do this for
250 # ($act eq "create") files?
251 my (undef, $dirs, undef) = File::Spec->splitpath($file);
252 my $absdir = $usepath;
255 foreach $d (File::Spec->splitdir($dirs)) {
257 $absdir = File::Spec->catdir($absdir, $d);
258 $reldir = $reldir ? File::Spec->catdir($reldir, $d) : $d;
259 if (! -d $absdir && ! $seen_dir{$reldir}) {
260 $seen_dir{$reldir} = 1;
261 push(@mkdir_list, $reldir);
265 push(@copy_list, $file);
267 if ($act eq "create") {
268 push(@add_list, $file);
270 } elsif ($act eq "remove") {
271 push(@remove_list, $file);
273 print STDERR "Unsure how to '$act' the '$file' file.\n";
277 # Now go through and mkdir() the directories,
278 # adding them to the CVS tree as we do.
281 printit qq(# The following "mkdir" and "cvs -Q add" calls are not\n),
282 qq(# necessary for any directories that already exist in the\n),
283 qq(# CVS tree but which aren't present locally.\n);
285 foreach (@mkdir_list) {
287 printit qq(if test ! -d $_; then\n);
291 execute(qq(cvs -Q add $_));
298 printit qq(# End of directory creation.\n);
302 # Copy in any files in the change, before we try to "cvs add" them.
308 execute(qq(cvs -Q add @add_list));
312 execute(qq(rm -f @remove_list));
313 execute(qq(cvs -Q remove @remove_list));
316 # Last, commit the whole bunch.
317 $commit = qq(cvs -Q commit -m "$summary" .);
319 printit qq(# Execute the following to commit the changes:\n),
332 foreach $dir (@cleanup) {
333 printit "rm -rf $dir\n";
336 # print STDERR "unlink($_)\n" if (!-d $_);
337 # print STDERR "rmdir($_)\n" if (-d $_ && $_ ne ".");
338 unlink($_) if (!-d $_);
339 rmdir($_) if (-d $_ && $_ ne ".");
342 rmdir($dir) || print STDERR "Could not remove $dir: $!\n";
351 ae2cvs - convert an Aegis change set to CVS commands
355 ae2cvs [-aedist|-aegis] [-c change] [-f file] [-l lib]
356 [-m module] [-n] [-p proj] [-q] [-u dir] [-x] [-X]
358 -aedist use aedist format from input (default)
359 -aegis query aegis repository directly
360 -c change change number
361 -f file read aedist from file ('-' == stdin)
362 -l lib Aegis library directory
366 -q quiet, don't print commands
367 -u dir use dir for CVS checkin
368 -x execute the commands, but don't commit;
369 two or more -x options commit changes
370 -X execute the commands and commit changes
374 The C<ae2cvs> utility can convert an Aegis change into a set of CVS (and
375 other) commands to make the corresponding change(s) to a carbon-copy CVS
376 repository. This can be used to keep a front-end CVS repository in sync
377 with changes made to an Aegis project, either manually or automatically
378 using the C<integrate_pass_notify_command> attribute of the Aegis
381 By default, C<ae2cvs> makes no changes to any software, and only prints
382 out the necessary commands. These commands can be examined first for
383 safety, and then fed to any Bourne shell variant (sh, ksh, or bash) to
384 make the actual CVS changes.
386 An option exists to have C<ae2cvs> execute the commands directly.
390 The C<ae2cvs> utility supports the following options:
396 Reads an aedist change set.
397 By default, the change set is read from standard input,
398 or a file specified with the C<-f> option.
402 Reads the change directly from the Aegis repository
403 by executing the proper C<aegis> commands.
407 Specify the Aegis change number to be used.
408 The value of the C<AEGIS_CHANGE> environment variable
413 Reads the aedist change set from the specified C<file>,
414 or from standard input if C<file> is C<'-'>.
418 Specifies an Aegis library directory to be searched for global states
419 files and user state files.
423 Specifies the name of the CVS module to be brought up-to-date.
424 The default is to use the Aegis project name,
425 minus any branch numbers;
426 for example, given an Aegis project name of C<foo-cmd.0.1>,
427 the default CVS module name is C<foo-cmd>.
431 No execute. Commands are printed (including a command for a final
432 commit of changes), but not executed. This is the default.
436 Specifies the name of the Aegis project from which this change is taken.
437 The value of the C<AEGIS_PROJECT> environment variable
442 Quiet. Commands are not printed.
446 Use the already checked-out CVS tree that exists at C<dir>
447 for the checkins and commits.
448 The default is to use a separately-created temporary directory.
452 Execute the commands to bring the CVS repository up to date,
453 except for the final commit of the changes. Two or more
454 C<-x> options will cause the change to be committed.
458 Execute the commands to bring the CVS repository up to date,
459 including the final commit of the changes.
463 =head1 ENVIRONMENT VARIABLES
469 Specifies any options to be used to initialize
470 the C<ae2cvs> utility.
471 Options on the command line override these values.
477 Steven Knight (knight at baldmt dot com)
481 If errors occur during the execution of the Aegis or CVS commands, and
482 the -X option is used, a partial change (consisting of those files for
483 which the command(s) succeeded) will be committed. It would be safer to
484 generate code to detect the error and print a warning.
486 When a file has been deleted in Aegis, the standard whiteout file can
487 cause a regex failure in this script. It doesn't necessarily happen all
488 the time, though, so this needs more investigation.
492 Add support for the CVS -d option to allow use of a specified
495 Add an explicit test for using ae2cvs in the Aegis
496 integrate_pass_notify_command field to support fully keeping a
497 repository in sync automatically.
501 Copyright 2001, 2002, 2003 Steven Knight.