2 # A simple system for making software releases
3 # (c) 2003--2011 Martin Mares <mj@ucw.cz>
13 my ($class,$basename) = @_;
15 "PACKAGE" => $basename,
17 # p=preprocess, s=subst, -=discard
18 '(^|/)(CVS|\.arch-ids|{arch}|\.git|tmp)/' => '-',
19 '\.(lsm|spec)$' => 'ps',
26 "DATE" => `date '+%Y-%m-%d' | tr -d '\n'`,
27 "LSMDATE" => `date '+%y%m%d' | tr -d '\n'`,
30 "archivedir" => "/home/mj/tmp/archives/$basename",
34 "testclean" => "make distclean",
46 sub GetVersionFromFile($) {
47 my ($s,$file,$rx) = @_;
48 open F, $file or die "Unable to open $file for version autodetection";
53 print "Detected version $1 from $file\n" if $verbose;
58 if (!defined $s->{"VERSION"}) { die "Failed to auto-detect version"; }
59 return $s->{"VERSION"};
62 sub GetVersionsFromChangelog($) {
63 my ($s,$file,$rx) = @_;
64 open F, $file or die "Unable to open $file for version autodetection";
68 if (!defined $s->{"VERSION"}) {
70 print "Detected version $1 from $file\n" if $verbose;
71 } elsif ($s->{"VERSION"} eq $1) {
74 $s->{"OLDVERSION"} = $1;
75 print "Detected previous version $1 from $file\n" if $verbose;
81 if (!defined $s->{"VERSION"}) { die "Failed to auto-detect version"; }
82 return $s->{"VERSION"};
87 $s->{"DISTDIR"} = $dd;
88 print "Initializing dist directory $dd\n" if $verbose;
89 `rm -rf $dd`; die if $?;
90 `mkdir -p $dd`; die if $?;
95 if (defined $s->{$v}) {
98 die "Reference to unknown variable $v";
103 my ($s,$f,$dir,$action) = @_;
105 (my $d = $f) =~ s@(^|/)[^/]*$@@;
107 -d $d || `mkdir -p $d`; die if $?;
109 my $preprocess = ($action =~ /p/);
110 my $subst = ($action =~ /s/);
111 if ($preprocess || $subst) {
112 open I, "$f" or die "open($f): $?";
113 open O, ">$dir/$f" or die "open($dir/$f): $!";
114 my @ifs = (); # stack of conditions, 1=satisfied
115 my $empty = 0; # last line was empty
116 my $is_makefile = ($f =~ /(Makefile|.mk)$/);
119 s/@([0-9A-Za-z_]+)@/$s->ExpandVar($1)/ge;
122 if (/^#/ || $is_makefile) {
123 if (/^#?ifdef\s+(\w+)/) {
124 if (defined ${$s->{"conditions"}}{$1}) {
125 push @ifs, ${$s->{"conditions"}}{$1};
129 } elsif (/^#ifndef\s+(\w+)/) {
130 if (defined ${$s->{"conditions"}}{$1}) {
131 push @ifs, -${$s->{"conditions"}}{$1};
135 } elsif (/^#if\s+/) {
137 } elsif (/^#?endif/) {
139 defined $x or die "Improper nesting of conditionals";
141 } elsif (/^#?else/) {
143 defined $x or die "Improper nesting of conditionals";
148 @ifs && $ifs[$#ifs] < 0 && next;
152 } else { $empty = 0; }
158 ! -x $f or chmod(0755, "$dir/$f") or die "chmod($dir/$f): $!";
160 `cp -a "$f" "$dir/$f"`; die if $?;
166 $s->{"PKG"} = $s->{"PACKAGE"} . "-" . $s->{"VERSION"};
167 my $dd = $s->{"DISTDIR"};
168 my $pkg = $s->{"PKG"};
169 my $dir = "$dd/$pkg";
170 print "Generating $dir\n";
172 FILES: foreach my $f (`find . -type f`) {
176 my @rules = @{$s->{"rules"}};
178 my $rule = shift @rules;
179 my $act = shift @rules;
185 ($action =~ /-/) && next FILES;
186 print "$f ($action)\n" if $verbose;
187 $s->CopyFile($f, $dir, $action);
190 foreach my $d (@{$s->{"directories"}}) {
191 `mkdir -p $dir/$d`; die if $?;
194 if (-f "$dir/Makefile") {
195 print "Cleaning up\n";
196 `cd $dir && make distclean >&2`; die if $?;
199 print "Creating $dd/$pkg.tar.gz\n";
200 my $tarvv = $verbose ? "vv" : "";
201 `cd $dd && tar cz${tarvv}f $pkg.tar.gz $pkg >&2`; die if $?;
202 push @{$s->{"distfiles"}}, "$dd/$pkg.tar.gz";
204 if ($s->{'do_sign'}) {
205 print "Signing package\n";
206 system "gpg", "--armor", "--detach-sig", "$dd/$pkg.tar.gz";
208 rename "$dd/$pkg.tar.gz.asc", "$dd/$pkg.tar.gz.sign" or die "No signature produced!?\n";
209 push @{$s->{"distfiles"}}, "$dd/$pkg.tar.gz.sign";
212 my $adir = $s->{"archivedir"};
213 my $afile = "$adir/$pkg.tar.gz";
214 print "Archiving to $afile\n";
215 -d $adir or `mkdir -p $adir`;
216 `cp $dd/$pkg.tar.gz $afile`; die if $?;
223 my $sf = $s->{"DISTDIR"} . "/" . $s->{"PKG"} . "/$f";
224 my $df = $s->{"DISTDIR"} . "/$f";
225 print "Generating $df\n";
226 `cp $sf $df`; die if $?;
227 push @{$s->{"distfiles"}}, $df;
230 sub ParseOptions($) {
233 "verbose!" => \$verbose,
234 "test!" => \$s->{"do_test"},
235 "patch!" => \$s->{"do_patch"},
236 "diff-against=s" => \$s->{"diff_against"},
237 "upload!" => \$s->{"do_upload"},
238 "sign!" => \$s->{"do_sign"},
239 ) || die "Syntax: release [--verbose] [--test] [--nopatch] [--diff-against=<version>] [--noupload] [--nosign]";
244 my $dd = $s->{"DISTDIR"};
245 my $pkg = $s->{"PKG"};
246 my $log = "$dd/$pkg.log";
247 my $make = $s->{'testmake'};
248 my $clean = $s->{'testclean'};
249 print "Doing a test compilation\n";
250 `( cd $dd/$pkg && $make ) >$log 2>&1`;
251 die "There were errors. Please inspect $log" if $?;
252 `grep -q [Ww]arning $log`;
253 $? or print "There were warnings! Please inspect $log.\n";
254 print "Cleaning up\n";
255 `cd $dd/$pkg && $clean`; die if $?;
260 my $dd = $s->{"DISTDIR"};
261 my $pkg1 = $s->{"PKG"};
263 if ($s->{"diff_against"} ne "") {
264 $oldver = $s->{"diff_against"};
265 } elsif (defined $s->{"OLDVERSION"}) {
266 $oldver = $s->{"OLDVERSION"};
268 print "WARNING: No previous version known. No patch generated.\n";
271 my $pkg0 = $s->{"PACKAGE"} . "-" . $oldver;
273 my $oldarch = $s->{"archivedir"} . "/" . $pkg0 . ".tar.gz";
274 -f $oldarch or die "MakePatch: $oldarch not found";
275 print "Unpacking $pkg0 from $oldarch\n";
276 `cd $dd && tar xzf $oldarch`; die if $?;
278 my $diff = $s->{"PACKAGE"} . "-" . $oldver . "-" . $s->{"VERSION"} . ".diff.gz";
279 print "Creating a patch from $pkg0 to $pkg1: $diff\n";
280 `cd $dd && diff -ruN $pkg0 $pkg1 | gzip >$diff`; die if $?;
281 push @{$s->{"distfiles"}}, "$dd/$diff";
286 foreach my $u (@{$s->{"uploads"}}) {
287 my $url = $u->{"url"};
288 print "Upload to $url :\n";
290 my $filter = $u->{"filter"} || ".*";
291 foreach my $f (@{$s->{"distfiles"}}) {
297 print "<confirm> "; <STDIN>;
298 if ($url =~ m@^scp://([^/]+)(.*)@) {
304 my $cmd = "scp @files $host:$dir\n";
306 } elsif ($url =~ m@ftp://([^/]+)(.*)@) {
309 open FTP, "|ftp -v $host" or die;
310 print FTP "cd $dir\n";
311 foreach my $f (@files) {
312 (my $ff = $f) =~ s@.*\/([^/].*)@$1@;
313 print FTP "put $f $ff\n";
319 die "Don't know how to handle this URL scheme";
326 $s->Test if $s->{"do_test"};
327 $s->MakePatch if $s->{"do_patch"};
328 $s->Upload if $s->{"do_upload"};