- start support for arch linux, code mostly written by Nikolay Rysev <mad.f3ka@gmail...
authorMichael Schroeder <mls@suse.de>
Fri, 30 Mar 2012 15:17:19 +0000 (17:17 +0200)
committerMichael Schroeder <mls@suse.de>
Fri, 30 Mar 2012 15:18:38 +0000 (17:18 +0200)
Build.pm
Build/Arch.pm [new file with mode: 0644]
Makefile
createarchdeps [new file with mode: 0755]
createrepomddeps

index a2ba443..7d56dbe 100644 (file)
--- a/Build.pm
+++ b/Build.pm
@@ -10,20 +10,25 @@ our $expand_dbg;
 our $do_rpm;
 our $do_deb;
 our $do_kiwi;
+our $do_arch;
 
 sub import {
   for (@_) {
     $do_rpm = 1 if $_ eq ':rpm';
     $do_deb = 1 if $_ eq ':deb';
     $do_kiwi = 1 if $_ eq ':kiwi';
+    $do_arch = 1 if $_ eq ':arch';
   }
-  $do_rpm = $do_deb = $do_kiwi = 1 if !$do_rpm && !$do_deb && !$do_kiwi;
+  $do_rpm = $do_deb = $do_kiwi = $do_arch = 1 if !$do_rpm && !$do_deb && !$do_kiwi && !$do_arch;
   if ($do_deb) {
     require Build::Deb;
   }
   if ($do_kiwi) {
     require Build::Kiwi;
   }
+  if ($do_arch) {
+    require Build::Arch;
+  }
 }
 
 my $std_macros = q{
@@ -854,6 +859,7 @@ sub parse {
   return Build::Deb::parse($cf, $fn, @args) if $do_deb && $fn =~ /\.dsc$/;
   return Build::Kiwi::parse($cf, $fn, @args) if $do_kiwi && $fn =~ /config\.xml$/;
   return Build::Kiwi::parse($cf, $fn, @args) if $do_kiwi && $fn =~ /\.kiwi$/;
+  return Build::Arch::parse($cf, $fn, @args) if $do_arch && $fn =~ /PKGBUILD$/;
   return undef;
 }
 
@@ -867,6 +873,7 @@ sub query {
   return Build::Rpm::query($handle, %opts) if $do_rpm && $binname =~ /\.rpm$/;
   return Build::Deb::query($handle, %opts) if $do_deb && $binname =~ /\.deb$/;
   return Build::Kiwi::queryiso($handle, %opts) if $do_kiwi && $binname =~ /\.iso$/;
+  return Build::Arch::query($handle, %opts) if $do_arch && $binname =~ /\.pkg\.tar(?:\.gz|\.xz)?$/;
   return undef;
 }
 
@@ -877,6 +884,7 @@ sub queryhdrmd5 {
   return Build::Kiwi::queryhdrmd5(@_) if $do_kiwi && $binname =~ /\.iso$/;
   return Build::Kiwi::queryhdrmd5(@_) if $do_kiwi && $binname =~ /\.raw$/;
   return Build::Kiwi::queryhdrmd5(@_) if $do_kiwi && $binname =~ /\.raw.install$/;
+  return Build::Arch::queryhdrmd5(@_) if $do_arch && $binname =~ /\.pkg\.tar(?:\.gz|\.xz)?$/;
   return undef;
 }
 
diff --git a/Build/Arch.pm b/Build/Arch.pm
new file mode 100644 (file)
index 0000000..a00bd71
--- /dev/null
@@ -0,0 +1,162 @@
+package Build::Arch;
+
+use strict;
+use Digest::MD5;
+use Data::Dumper;
+use Archive::Tar;
+
+# Archlinux support, based on the GSoC work of Nikolay Rysev <mad.f3ka@gmail.com>
+
+# parse a PKGBUILD file
+
+sub quote {
+  my ($str) = @_;
+  $str =~ s/([ \t\"\'])/sprintf("%%%02X", ord($1))/ge;
+  return $str;
+}
+
+sub unquotesplit {
+  my ($str) = @_;
+  $str =~ s/%/%25/g;
+  $str =~ s/^[ \t]+//;
+  while ($str =~ /([\"\'])/) {
+    my $q = $1;
+    $str =~ s/$q(.*?)$q/quote($1)/e;
+  }
+  my @args = split(/[ \t]+/, $str);
+  for (@args) {
+    s/%([a-fA-F0-9]{2})/chr(hex($1))/ge
+  }
+  return @args;
+}
+
+sub parse {
+  my ($config, $pkgbuild) = @_;
+  my $ret;
+  local *PKG;
+  if (!open(PKG, '<', $pkgbuild)) {
+    $ret->{'error'} = "$pkgbuild: $!";
+    return $ret;
+  }
+  my %vars;
+  while (<PKG>) {
+    chomp;
+    next if /^\s*$/;
+    next if /^\s*#/;
+    last unless /^([a-zA-Z0-9_]*)=(\(?)(.*?)$/;
+    my $var = $1;
+    my $val = $3;
+    if ($2) {
+      while ($val !~ s/\)\s*$//s) {
+       my $nextline = <PKG>;
+       last unless defined $nextline;
+       chomp $nextline;
+       $val .= ' ' . $nextline;
+      }
+    }
+    $vars{$var} = [ unquotesplit($val) ];
+  }
+  close PKG;
+  $ret->{'name'} = $vars{'pkgname'}->[0] if $vars{'pkgname'};
+  $ret->{'version'} = $vars{'pkgver'}->[0] if $vars{'pkgver'};
+  $ret->{'deps'} = $vars{'makedepends'} || [];
+  return $ret;
+}
+
+sub query {
+  my ($handle, %opts) = @_;
+  if (ref($handle)) {
+    die("arch pkg query not implemented for file handles\n");
+  }
+  if ($handle =~ /\.xz$/) {
+    my $nh;
+    open($nh, '-|', 'xzdec', '-dc', $handle) || die("$handle: $!\n");
+    $handle = $nh;
+  }
+  my $tar = Archive::Tar->new;
+  my @read = $tar->read($handle, 1, {'filter' => '^\.PKGINFO$', 'limit' => 1});
+  die("$handle: not an arch package file\n") unless @read ==  1;
+  my $pkginfo = $read[0]->get_content;
+  die("$handle: not an arch package file\n") unless $pkginfo;
+  my %vars;
+  for my $l (split('\n', $pkginfo)) {
+    next unless $l =~ /^(.*?) = (.*)$/;
+    push @{$vars{$1}}, $2;
+  }
+  my $ret = {};
+  $ret->{'name'} = $vars{'pkgname'}->[0] if $vars{'pkgname'};
+  $ret->{'hdrmd5'} = Digest::MD5::md5_hex($pkginfo);
+  $ret->{'provides'} = $vars{'provides'} || [];
+  $ret->{'requires'} = $vars{'depends'} || [];
+  if ($vars{'pkgname'}) {
+    my $selfprovides = $vars{'pkgname'}->[0];
+    $selfprovides .= "=$vars{'pkgver'}->[0]" if $vars{'pkgver'};
+    push @{$ret->{'provides'}}, $selfprovides unless @{$ret->{'provides'} || []} && $ret->{'provides'}->[-1] eq $selfprovides;
+  }
+  if ($opts{'evra'}) {
+    if ($vars{'pkgver'}) {
+      my $evr = $vars{'pkgver'}->[0];
+      if ($evr =~ /^([0-9]+):(.*)$/) {
+       $ret->{'epoch'} = $1;
+       $evr = $2;
+      }
+      $ret->{'version'} = $evr;
+      if ($evr =~ /^(.*)-(.*?)$/) {
+       $ret->{'version'} = $1;
+       $ret->{'release'} = $2;
+      }
+    }
+    $ret->{'arch'} = $vars{'arch'}->[0] if $vars{'arch'};
+  }
+  if ($opts{'description'}) {
+    $ret->{'description'} = $vars{'pkgdesc'}->[0] if $vars{'pkgdesc'};
+  }
+  return $ret;
+}
+
+sub queryhdrmd5 {
+  my ($handle) = @_;
+  if (ref($handle)) {
+    die("arch pkg query not implemented for file handles\n");
+  }
+  if ($handle =~ /\.xz$/) {
+    my $nh;
+    open($nh, '-|', 'xzdec', '-dc', $handle) || die("$handle: $!\n");
+    $handle = $nh;
+  }
+  my $tar = Archive::Tar->new;
+  my @read = $tar->read($handle, 1, {'filter' => '^\.PKGINFO$', 'limit' => 1});
+  die("$handle: not an arch package file\n") unless @read ==  1;
+  my $pkginfo = $read[0]->get_content;
+  die("$handle: not an arch package file\n") unless $pkginfo;
+  return Digest::MD5::md5_hex($pkginfo);
+}
+
+sub parserepodata {
+  my ($d, $data) = @_;
+  $d ||= {};
+  $data =~ s/^\n+//s;
+  my @parts = split(/\n\n+/s, $data);
+  for my $part (@parts) {
+    my @p = split("\n", $part);
+    my $p = shift @p;
+    if ($p eq '%NAME%') {
+      $d->{'name'} = $p[0];
+    } elsif ($p eq '%VERSION%') {
+      $d->{'version'} = $p[0];
+    } elsif ($p eq '%ARCH%') {
+      $d->{'arch'} = $p[0];
+    } elsif ($p eq '%BUILDDATE%') {
+      $d->{'buildtime'} = $p[0];
+    } elsif ($p eq '%FILENAME%') {
+      $d->{'filename'} = $p[0];
+    } elsif ($p eq '%PROVIDES%') {
+      push @{$d->{'provides'}}, @p;
+    } elsif ($p eq '%DEPENDS%') {
+      push @{$d->{'requires'}}, @p;
+    }
+  }
+  return $d;
+}
+
+1;
index aff4aff..ff34b8a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -48,6 +48,7 @@ install:
            debtransformzip \
            mkbaselibs \
            mkdrpms \
+           createarchdeps \
            createrepomddeps \
            createyastdeps \
            changelog2spec \
diff --git a/createarchdeps b/createarchdeps
new file mode 100755 (executable)
index 0000000..7964e2f
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/perl -w
+
+# Archlinux support, based on the GSoC work of Nikolay Rysev <mad.f3ka@gmail.com>
+
+BEGIN {
+  unshift @INC, ($::ENV{"BUILD_DIR"} || "/usr/lib/build");
+}
+
+use strict;
+use Archive::Tar;
+use Build::Arch;
+use Digest::MD5;
+use File::Path;
+
+my $cachedir = "/var/cache/build";
+
+sub getrepodb {
+  my ($url, $reponame, $dir) = @_;
+  File::Path::mkpath($dir);
+  system($INC[0]."/download", $dir, "$url$reponame.db");
+}
+
+sub getreponame {
+  my ($url) = @_;
+  return $1 if "/$url/" =~ /.*\/([^\/]+)\/os\//;
+  return undef;
+}
+
+sub printpkginfo {
+  my ($d, $repourl)  = @_;
+  my $id = $d->{'name'} . "." . $d->{'arch'} . "-" . $d->{'buildtime'} . "/0/0";
+  my $pkgurl = $repourl . $d->{'filename'};
+  my $selfprovides = $d->{'name'};
+  $selfprovides .= "=$d->{'version'}" if defined $d->{'version'};
+  push @{$d->{'provides'}}, $selfprovides unless @{$d->{'provides'} || []} && $d->{'provides'}->[-1] eq $selfprovides;
+  print "F:$id: $pkgurl\n";
+  print "P:$id: " . join(' ', @{$d->{'provides'}}) . "\n" if $d->{'provides'};
+  print "R:$id: " . join(' ', @{$d->{'requires'}}) . "\n" if $d->{'requires'};
+  print "I:$id: $d->{name}-$d->{'version'} $d->{'buildtime'}\n";
+}
+
+for my $url (@ARGV) {
+  die("Not an Archlinux repo") unless $url =~ /^(ht|f)tp:\/\/([^\/]*)\/?/;
+  $url .= '/' unless $url =~ /\/$/;
+  my $reponame = getreponame($url);
+
+  my $repoid = Digest::MD5::md5_hex($url);
+  my $dir = "$cachedir/$repoid";
+  getrepodb($url, $reponame, $dir);
+
+  my $repodb = Archive::Tar->iter("$dir/$reponame.db", 1);
+  my $e;
+  my $lastfn = '';
+  my $d;
+  while ($e = $repodb->()) {
+    next unless $e->type() == Archive::Tar::Constant::FILE;
+    my $fn = $e->name();
+    next unless $fn =~ s/\/(?:depends|desc)$//s;
+    if ($lastfn ne $fn) {
+      printpkginfo($d, $url) if $d->{'name'};
+      $d = {};
+      $lastfn = $fn;
+    }
+    Build::Arch::parserepodata($d, $e->get_content());
+  }
+  printpkginfo($d, $url) if $d->{'name'};
+}
index 2aa1946..aaff8fb 100755 (executable)
@@ -402,6 +402,8 @@ for my $url (@ARGV) {
     $baseurl = $url;
     $baseurl .= '/' unless $baseurl =~ /\/$/;
     getmetadata($baseurl, $dir);
+  } elsif ($url =~ /^arch\@/) {
+    exec ($INC[0].'/createarchdeps', substr($url, 5));
   } else {
     $dir = $url;
     $dir .= '/' unless $dir =~ /\/$/;