3 ################################################################
5 # Copyright (c) 1995-2014 SUSE Linux Products GmbH
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License version 2 or 3 as
9 # published by the Free Software Foundation.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program (see the file COPYING); if not, write to the
18 # Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 ################################################################
28 die("usage: debtransform [--debug] [--changelog <changelog>] [--release <release number>] <srcdir> <dscfile> <outdir>\n");
35 open(F, '<', $fn) || die("Error in reading $fn: $!\n");
39 splice(@control, 0, 3) if @control > 3 && $control[0] =~ /^-----BEGIN/;
43 my $c = shift @control;
44 last if $c eq ''; # new paragraph
45 my ($tag, $data) = split(':', $c, 2);
46 next unless defined $data;
49 while (@control && $control[0] =~ /^\s/) {
50 $data .= "\n".substr(shift @control, 1);
56 $tag{'__seq'} = \@seq;
62 print "Writing $fn\n";
63 open(F, '>', $fn) || die("open $fn: $!\n");
64 my @seq = @{$tags->{'__seq'} || []};
65 my %seq = map {uc($_) => 1} @seq;
66 for (sort keys %$tags) {
67 push @seq, ucfirst(lc($_)) unless $seq{$_};
71 my $d = $tags->{$ucseq};
72 next unless defined $d;
81 my ($tar, $skipdebiandir) = @_;
82 print "Scanning $tar...\n";
85 unless(defined($skipdebiandir)) {
88 open(F, '-|', 'tar', '--numeric-owner', '-tvf', $tar) ||
89 die("Execution of tar subprocess failed: $!\n");
91 next unless /^([-dlbcp])(.........)\s+\d+\/\d+\s+(\S+) \d\d\d\d-\d\d-\d\d \d\d:\d\d(?::\d\d)? (.*)$/;
92 my ($type, $mode, $size, $name) = ($1, $2, $3, $4);
95 next if $skipdebiandir eq 0;
96 die("Archive contains a link: $name\n");
99 next if $skipdebiandir eq 0;
100 die("Archive contains an unexpected type for file \"$name\"\n");
103 $name =~ s/^debian\/// if $skipdebiandir eq 1;
104 push @c, {'name' => $name, 'size' => $size};
106 close(F) || die("tar exited with non-zero status: $!\n");
111 my ($tar, $filename, $s) = @_;
113 print "Extracting $tar...\n";
114 open(F, '-|', 'tar', '-xOf', $tar, $filename) ||
115 die("Execution of tar subprocess failed: $!\n");
118 my $l = sysread(F, $file, $s, length($file));
119 die("Error while reading from tar subprocess: $!\n") unless $l;
122 my @file = split("\n", $file);
123 close(F) || warn("tar exited with non-zero status: $!\n");
128 my ($oldname, $newname, $origtarfile, @content) = @_;
130 for my $c (@{$origtarfile->{'content'}}) {
131 if ($c->{'name'} eq $newname) {
132 @oldcontent = extracttar($origtarfile->{'name'}, $c->{'name'}, $c->{'size'});
135 if ($newname eq $origtarfile->{'tardir'}."/debian/changelog") {
136 my $firstline = $content[0];
137 my $version = $firstline;
138 $version =~ s/.*\((.*)\).*/$1/g;
139 if ($version ne $origtarfile->{'version'}) {
140 $firstline =~ s/\(.*\)/($origtarfile->{'version'})/g;
141 my $date = `date -R`;
143 my @newcontent = ($firstline, "", " * version number update by debtransform", "", " -- debtransform <build\@opensuse.org> ".$date, "");
144 push(@newcontent, @content);
145 @content = @newcontent;
148 return unless @content;
149 print DIFF "--- $oldname\n";
150 print DIFF "+++ $newname\n";
152 print DIFF "\@\@ -1,".scalar(@oldcontent)." +1,".scalar(@content)." \@\@\n";
153 print DIFF "-$_\n" for @oldcontent;
155 print DIFF "\@\@ -0,0 +1,".scalar(@content)." \@\@\n";
157 print DIFF "+$_\n" for @content;
161 my ($tar, $tardir, $origin, $origtarfile, @c) = @_;
163 open(F, '-|', 'tar', '-xOf', $tar) || die("tar: $!\n");
165 my $s = $c->{'size'};
168 my $l = sysread(F, $file, $s, length($file));
169 die("tar read error\n") unless $l;
172 next if $origin && $origin->{$c->{'name'}} ne $tar;
173 my @file = split("\n", $file);
174 dodiff("$tardir.orig/debian/$c->{'name'}", "$tardir/debian/$c->{'name'}", $origtarfile, @file);
180 my ($file, $tardir, $dfile, $origtarfile) = @_;
182 print "Processing file \"$file\"...\n";
183 open(F, '<', $file) || die("Error in reading $file: $!\n");
187 dodiff("$tardir.orig/$dfile", "$tardir/$dfile", $origtarfile, @file);
191 my ($series, $tardir) = @_;
195 $dir = '.' if $dir eq '';
197 open(F, '<', $series) || die("$series: $!\n");
201 print "Processing series file \"$series\"...\n";
202 for my $patch (@series) {
203 $patch =~ s/(^|\s+)#.*//;
204 next if $patch =~ /^\s*$/;
206 $level = $1 if $patch =~ /\s.*-p\s*(\d+)/;
208 print "Processing patch $dir/$patch...\n";
209 open(F, '<', "$dir/$patch") || die("Error in reading $dir/$patch: $!\n");
212 if ((/^--- ./ || /^\+\+\+ ./) && !/^... \/dev\/null/) {
213 my $start = substr($_, 0, 4);
217 last unless s/.*?\///;
220 if ($start eq '--- ') {
221 print DIFF "$start$tardir.orig/$_\n";
223 print DIFF "$start$tardir/$_\n";
238 open(F, '<', $file) || die("Error in reading $file: $!\n");
240 my $ctx = Digest::MD5->new;
243 my $md5 = $ctx->hexdigest();
244 return "$md5 $size $base";
247 print "** Started: debtransform @ARGV\n";
254 if ($ARGV[0] eq '--debug') {
257 } elsif ($ARGV[0] eq '--changelog') {
259 $changelog = shift @ARGV;
260 } elsif ($ARGV[0] eq '--release') {
262 $release = shift @ARGV;
276 die("$out is not a directory\n") unless -d $out;
278 my $tags = parsedsc($dsc);
280 opendir(D, $dir) || die("Could not open $dir: $!\n");
281 my @dir = grep {$_ ne '.' && $_ ne '..'} readdir(D);
283 my %dir = map {$_ => 1} @dir;
285 my $tarfile = $tags->{'DEBTRANSFORM-TAR'};
287 if ($tags->{'DEBTRANSFORM-FILES-TAR'}) {
288 @debtarfiles = split(' ', $tags->{'DEBTRANSFORM-FILES-TAR'});
291 if (!$tarfile || !@debtarfiles) {
292 my @tars = grep {/\.tgz$|\.tar(?:\.gz|\.bz2|\.xz)?$/} @dir;
293 my @debtars = grep {/^debian\.tar(?:\.gz|\.bz2|\.xz)?$/} @tars;
295 print "No DEBTRANSFORM-TAR line in the .dsc file.\n";
296 print "Attempting automatic discovery of a suitable source archive.\n";
297 @tars = grep {!/^debian\.tar(?:\.gz|\.bz2|\.xz)?$/} @tars;
299 my %debtarfiles = map {$_ => 1} @debtarfiles;
300 @tars = grep {!$debtarfiles{$_}} @tars;
302 die("None of the files looks like a usable source tarball.\n") unless @tars;
303 die("Too many files looking like a usable source tarball (would not know which to pick): @tars\n") if @tars > 1;
305 print "Source archive chosen for transformation: $tarfile\n";
307 if (!exists($tags->{'DEBTRANSFORM-FILES-TAR'})) {
308 print "No DEBTRANSFORM-FILES-TAR line in the .dsc file.\n";
309 print "Attempting automatic discovery of a debian archive.\n";
311 if (@debtars && !exists($tags->{'DEBTRANSFORM-FILES-TAR'})) {
312 die("package contains more than one debian archive\n") if @debtars > 1;
313 @debtarfiles = ($debtars[0]);
314 print "Debian archive chosen for transformation: $debtars[0]\n";
318 my $name = $tags->{'SOURCE'};
319 die("dsc file contains no Source: line\n") unless defined($name);
320 my $version = $tags->{'VERSION'};
321 die("dsc file contains no Version: line\n") unless defined($version);
322 # no epoch in version, please
323 if ($version =~ s/^\d+://) {
324 print "Stripped epoch from Version field, which is now \"$version\".\n";
328 # debtransform will always generate a 1.0 format type,
329 # so it has to transform all source archives into weak gzip files.
331 if ($tarfile =~ /\.tar\.bz2/) {
333 $tarfile =~ s/\.tar\.bz2/\.tar\.gz/;
334 $tmptar = "$out/$tarfile";
335 print "converting $dir/$old to $tarfile\n";
336 system( ( "debtransformbz2", "$dir/$old", "$tmptar" )) == 0 || die("cannot transform .tar.bz2 to .tar.gz");
338 if ($tarfile =~ /\.tar\.xz/) {
340 $tarfile =~ s/\.tar\.xz/\.tar\.gz/;
341 $tmptar = "$out/$tarfile";
342 print "converting $dir/$old to $tarfile\n";
343 system( ( "debtransformxz", "$dir/$old", "$tmptar" )) == 0 || die("cannot transform .tar.xz to .tar.gz");
345 if ($tarfile =~ /\.zip/) {
347 $tarfile =~ s/\.zip/\.tar\.gz/;
348 $tmptar = "$out/$tarfile";
349 print "converting $dir/$old to $tarfile\n";
350 system( ( "debtransformzip", "$dir/$old", "$tmptar" )) == 0 || die("cannot transform .zip to .tar.gz");
352 if ($tarfile =~ /\.tgz$/) {
354 $tarfile =~ s/\.tgz/\.tar.gz/;
355 $tmptar = "$out/$tarfile";
356 print "renaming $dir/$old to $tarfile\n";
357 system ( ("mv", "$dir/$old", "$tmptar" ) ) == 0 || die("cannot rename .tgz to .tar.gz");
363 $tarfile =~ /.*(\.tar.*?)$/;
364 my $ntarfile = "${name}_$v.orig$1";
366 print "Moving $dir/$tarfile to $out/$ntarfile\n";
367 link("$tmptar", "$out/$ntarfile") || die("link: $!\n");
370 print "Hardlinking $dir/$tarfile to $out/$ntarfile\n";
371 link("$dir/$tarfile", "$out/$ntarfile") || die("link: $!\n");
373 push @files, addfile("$out/$ntarfile");
374 print "files @files\n";
376 if ( $tags->{'DEBTRANSFORM-RELEASE'} && $release ) {
377 # if dsc file contains the tag DEBTRANSFORM-RELEASE
378 # and "release" is given as a commad line parameter,
379 # replace "version" from dsc file by "version-release".
380 # From "version" the current release is stripped before
381 # (last "-" and the part after the last "-").
382 # On OBS, release is incremented automatically
384 $version = $v . "-" . $release;
385 $tags->{'VERSION'} = $version;
386 print "Modifying dsc Version field to \"$tags->{VERSION}\"\n";
389 my $tarpath = "$out/$ntarfile";
390 my $tardir = $tarfile;
391 $tardir =~ s/\.orig\.tar/\.tar/;
392 $tardir =~ s/\.tar.*?$//;
393 my @tarfilecontent = listtar($tarpath, 0);
394 my $origtarfile = {'name' => $tarpath, 'content' => \@tarfilecontent, 'version' => $tags->{'VERSION'}, 'tardir' => $tardir};
396 print "Generating $out/${name}_$version.diff\n";
397 # Since we are generating a unitary diff, we must re-set Format:.
398 $tags->{"FORMAT"} = "1.0";
399 open(DIFF, '>', "$out/${name}_$version.diff") || die("Cannot open $out/${name}_$version.diff for write: $!\n");
401 undef $changelog if $dir{'debian.changelog'};
405 for my $debtarfile (@debtarfiles) {
406 my @c = listtar("$dir/$debtarfile");
407 $debtarcontent{$debtarfile} = \@c;
409 die("\"$_->{'name'}\" exists in both the debian archive as well as the package source directory.\n") if $dir{"debian.$_->{'name'}"};
410 undef $changelog if $_->{'name'} eq 'changelog';
411 $debtarorigin{$_->{'name'}} = "$dir/$debtarfile";
415 dofile($changelog, $tardir, 'debian/changelog', $origtarfile) if defined $changelog;
417 if ($tags->{'DEBTRANSFORM-FILES'}) {
418 for my $file (split(' ', $tags->{'DEBTRANSFORM-FILES'})) {
419 dofile("$dir/$file", $tardir, $file, $origtarfile);
423 for my $debtarfile (@debtarfiles) {
424 dotar("$dir/$debtarfile", $tardir, \%debtarorigin, $origtarfile, @{$debtarcontent{$debtarfile} });
427 for my $file (grep {/^debian\./} @dir) {
428 next if $file eq 'debian.series';
429 next if $file =~ /\.tar$/;
430 next if $file =~ /\.tar\./;
431 dofile("$dir/$file", $tardir, 'debian/'.substr($file, 7), $origtarfile);
434 if ($tags->{'DEBTRANSFORM-SERIES'}) {
435 doseries("$dir/$tags->{'DEBTRANSFORM-SERIES'}", $tardir);
436 } elsif ($dir{"debian.series"}) {
437 doseries("$dir/debian.series", $tardir);
438 } elsif ($dir{"patches.series"}) {
439 doseries("$dir/patches.series", $tardir);
444 if (! -s "$out/${name}_$version.diff") {
445 unlink("$out/${name}_$version.diff");
447 system('gzip', '-9', "$out/${name}_$version.diff");
448 if (-f "$out/${name}_$version.diff.gz") {
449 push @files, addfile("$out/${name}_$version.diff.gz");
451 push @files, addfile("$out/${name}_$version.diff");
455 $tags->{'FILES'} = "\n".join("\n", @files);
456 delete $tags->{'DEBTRANSFORM-SERIES'};
457 delete $tags->{'DEBTRANSFORM-TAR'};
458 delete $tags->{'DEBTRANSFORM-FILES-TAR'};
459 delete $tags->{'DEBTRANSFORM-FILES'};
460 delete $tags->{'DEBTRANSFORM-RELEASE'};
461 writedsc("$out/${name}_$version.dsc", $tags);
465 print `cat $out/${name}_$version.dsc`;
466 print `zcat $out/${name}_$version.diff.gz`;