2 # A simple system for making software releases
3 # (c) 2003--2012 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',
23 # Symbols, which can serve as conditions for the preprocessor
25 "DATE" => `date '+%Y-%m-%d' | tr -d '\n'`,
26 "LSMDATE" => `date '+%y%m%d' | tr -d '\n'`,
28 # Files to be uploaded
31 # Locations where we want to upload, e.g.:
32 # { "url" => "ftp://metalab.unc.edu/incoming/linux/",
33 # "filter" => '(\.tar\.gz|\.lsm)$', }
35 "test_compile" => "make",
36 # "archive_dir" => "/tmp/archives/$basename",
50 print "<confirm> "; <STDIN>;
53 sub GetVersionFromGit($) {
55 return if defined $s->{"VERSION"};
56 my $desc = `git describe --tags`; die "git describe failed\n" if $?;
58 my ($ver, $rest) = ($desc =~ /^v([0-9.]+)(.*)/) or die "Failed to understand output of git describe: $desc\n";
59 print "Detected version $ver from git description $desc\n";
61 print "WARNING: We are several commits past release tag... ";
64 $s->{"VERSION"} = $ver;
68 sub GetVersionFromFile($) {
69 my ($s,$file,$rx) = @_;
70 return if defined $s->{"VERSION"};
71 open F, $file or die "Unable to open $file for version autodetection";
76 print "Detected version $1 from $file\n" if $verbose;
81 if (!defined $s->{"VERSION"}) { die "Failed to auto-detect version"; }
82 return $s->{"VERSION"};
85 sub GetVersionsFromChangelog($) {
86 my ($s,$file,$rx) = @_;
87 return if defined $s->{"VERSION"};
88 open F, $file or die "Unable to open $file for version autodetection";
92 if (!defined $s->{"VERSION"}) {
94 print "Detected version $1 from $file\n" if $verbose;
95 } elsif ($s->{"VERSION"} eq $1) {
98 $s->{"OLDVERSION"} = $1;
99 print "Detected previous version $1 from $file\n" if $verbose;
105 if (!defined $s->{"VERSION"}) { die "Failed to auto-detect version"; }
106 return $s->{"VERSION"};
111 $s->{"DISTDIR"} = $dd;
112 print "Initializing dist directory $dd\n" if $verbose;
113 `rm -rf $dd`; die if $?;
114 `mkdir -p $dd`; die if $?;
116 if ($s->{"archive_dir"}) {
117 unshift @{$s->{"uploads"}}, { "url" => "file:" . $s->{"archive_dir"} };
123 if (defined $s->{$v}) {
126 die "Reference to unknown variable $v";
130 sub TransformFile($$$) {
131 my ($s,$file,$action) = @_;
133 my $preprocess = ($action =~ /p/);
134 my $subst = ($action =~ /s/);
135 my $dest = "$file.dist";
136 open I, "<", $file or die "open($file): $?";
137 open O, ">", "$dest" or die "open($dest): $!";
138 my @ifs = (); # stack of conditions, 1=satisfied
139 my $empty = 0; # last line was empty
140 my $is_makefile = ($file =~ /(Makefile|.mk)$/);
143 s/@([0-9A-Za-z_]+)@/$s->ExpandVar($1)/ge;
146 if (/^#/ || $is_makefile) {
147 if (/^#?ifdef\s+(\w+)/) {
148 if (defined ${$s->{"conditions"}}{$1}) {
149 push @ifs, ${$s->{"conditions"}}{$1};
153 } elsif (/^#ifndef\s+(\w+)/) {
154 if (defined ${$s->{"conditions"}}{$1}) {
155 push @ifs, -${$s->{"conditions"}}{$1};
159 } elsif (/^#if\s+/) {
161 } elsif (/^#?endif/) {
163 defined $x or die "Improper nesting of conditionals";
165 } elsif (/^#?else/) {
167 defined $x or die "Improper nesting of conditionals";
172 @ifs && $ifs[$#ifs] < 0 && next;
176 } else { $empty = 0; }
182 ! -x $file or chmod(0755, "$dest") or die "chmod($dest): $!";
183 rename $dest, $file or "rename($dest,$file): $!";
188 $s->{"PKG"} = $s->{"PACKAGE"} . "-" . $s->{"VERSION"};
189 my $dd = $s->{"DISTDIR"};
190 my $pkg = $s->{"PKG"};
191 my $dir = "$dd/$pkg";
192 print "Generating $dir\n";
194 system "git archive --format=tar --prefix=$dir/ HEAD | tar xf -";
197 my @files = `cd $dir && find . -type f`;
204 my @rules = @{$s->{"rules"}};
206 my $rule = shift @rules;
207 my $act = shift @rules;
214 } elsif ($action =~ /-/) {
215 unlink "$dir/$f" or die "Cannot unlink $dir/$f: $!\n";
216 print "$f (unlinked)\n" if $verbose;
218 print "$f ($action)\n" if $verbose;
219 $s->TransformFile("$dir/$f", $action);
228 my $sf = $s->{"DISTDIR"} . "/" . $s->{"PKG"} . "/$f";
229 my $df = $s->{"DISTDIR"} . "/$f";
230 print "Generating $df\n";
231 `cp $sf $df`; die if $?;
232 push @{$s->{"distfiles"}}, $df;
237 $s->{'do_sign'} or return;
238 print "Signing $file\n";
239 system "gpg", "--armor", "--detach-sig", "$file";
241 rename "$file.asc", "$file.sign" or die "No signature produced!?\n";
242 push @{$s->{"distfiles"}}, "$file.sign";
247 my $dd = $s->{"DISTDIR"};
248 my $pkg = $s->{"PKG"};
250 print "Creating $dd/$pkg.tar\n";
251 my $tarvv = $verbose ? "vv" : "";
252 `cd $dd && tar c${tarvv}f $pkg.tar $pkg >&2`; die if $?;
254 print "Creating $dd/$pkg.tar.gz\n";
255 `gzip <$dd/$pkg.tar >$dd/$pkg.tar.gz`; die if $?;
256 push @{$s->{"distfiles"}}, "$dd/$pkg.tar.gz";
258 # print "Creating $dd/$pkg.tar.bz2\n";
259 # `bzip2 <$dd/$pkg.tar >$dd/$pkg.tar.bz2`; die if $?;
260 # push @{$s->{"distfiles"}}, "$dd/$pkg.tar.bz2";
262 $s->SignFile("$dd/$pkg.tar");
265 sub ParseOptions($) {
268 "verbose!" => \$verbose,
269 "test!" => \$s->{"do_test"},
270 "patch!" => \$s->{"do_patch"},
271 "diff-against=s" => \$s->{"diff_against"},
272 "version=s" => \$s->{"VERSION"},
273 "upload!" => \$s->{"do_upload"},
274 "sign!" => \$s->{"do_sign"},
275 ) || die "Syntax: release [--verbose] [--test] [--nopatch] [--version=<version>] [--diff-against=<version>] [--noupload] [--nosign]";
280 $s->{"do_test"} or return;
281 my $dd = $s->{"DISTDIR"};
282 my $pkg = $s->{"PKG"};
283 my $tdir = "$dd/$pkg.test";
284 $s->{"TESTDIR"} = $tdir;
285 `cp -a $dd/$pkg $tdir`; die if $?;
286 my $log = "$tdir.log";
287 print "Doing a test compilation\n";
288 my $make = $s->{"test_compile"};
289 `( cd $tdir && $make ) >$log 2>&1`;
290 die "There were errors. Please inspect $log" if $?;
291 `grep -q [Ww]arning $log`;
292 $? or print "There were warnings! Please inspect $log.\n";
297 $s->{"do_patch"} or return;
298 my $dd = $s->{"DISTDIR"};
299 my $pkg1 = $s->{"PKG"};
301 if ($s->{"diff_against"} ne "") {
302 $oldver = $s->{"diff_against"};
303 } elsif (defined $s->{"OLDVERSION"}) {
304 $oldver = $s->{"OLDVERSION"};
306 print "WARNING: No previous version known. No patch generated.\n";
309 my $pkg0 = $s->{"PACKAGE"} . "-" . $oldver;
311 my $oldarch = $s->{"archivedir"} . "/" . $pkg0 . ".tar.gz";
312 -f $oldarch or die "MakePatch: $oldarch not found";
313 print "Unpacking $pkg0 from $oldarch\n";
314 `cd $dd && tar xzf $oldarch`; die if $?;
316 my $diff = $s->{"PACKAGE"} . "-" . $oldver . "-" . $s->{"VERSION"} . ".diff.gz";
317 print "Creating a patch from $pkg0 to $pkg1: $diff\n";
318 `cd $dd && diff -ruN $pkg0 $pkg1 | gzip >$diff`; die if $?;
319 push @{$s->{"distfiles"}}, "$dd/$diff";
320 $s->SignFile("$dd/$diff");
325 $s->{"do_upload"} or return;
326 foreach my $u (@{$s->{"uploads"}}) {
327 my $url = $u->{"url"};
328 print "Upload to $url :\n";
330 my $filter = $u->{"filter"} || ".*";
331 foreach my $f (@{$s->{"distfiles"}}) {
338 if ($url =~ m@^file:(.*)@) {
341 `cp @files $dir/`; die if $?;
342 } elsif ($url =~ m@^scp://([^/]+)(.*)@) {
348 my $cmd = "scp @files $host:$dir\n";
350 } elsif ($url =~ m@ftp://([^/]+)(.*)@) {
353 open FTP, "|ftp -v $host" or die;
354 print FTP "cd $dir\n";
355 foreach my $f (@files) {
356 (my $ff = $f) =~ s@.*\/([^/].*)@$1@;
357 print FTP "put $f $ff\n";
363 die "Don't know how to handle this URL scheme";