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{
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;
}
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;
}
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;
}
--- /dev/null
+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;
--- /dev/null
+#!/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'};
+}