From 5ed2993e880cca5a86abbc67d1ac6249b45a4608 Mon Sep 17 00:00:00 2001 From: Yukari Chiba Date: Thu, 25 Sep 2025 15:12:09 +0800 Subject: [PATCH] Build::Archsrcinfo: use .SRCINFO as a choice of build recipe for Arch Introduce the capability to use `.SRCINFO` files as alternative build recipe for Arch Linux packages. Using static `.SRCINFO` files is a faster and more reliable method for extracting package metadata (like dependencies, version, and source URLs). --- Build.pm | 14 ++++- Build/Archsrcinfo.pm | 132 +++++++++++++++++++++++++++++++++++++++++++ Build/Repo.pm | 8 ++- PBuild/Job.pm | 1 + PBuild/Recipe.pm | 2 + build-recipe | 1 + 6 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 Build/Archsrcinfo.pm diff --git a/Build.pm b/Build.pm index 1fb8120f..fea47fc2 100644 --- a/Build.pm +++ b/Build.pm @@ -35,6 +35,7 @@ our $do_rpm; our $do_deb; our $do_kiwi; our $do_arch; +our $do_archsrcinfo; our $do_collax; our $do_livebuild; our $do_snapcraft; @@ -53,6 +54,7 @@ sub import { $do_rpm = 1 if $_ eq ':rpm'; $do_deb = 1 if $_ eq ':deb'; $do_kiwi = 1 if $_ eq ':kiwi'; + $do_archsrcinfo = 1 if $_ eq ':archsrcinfo'; $do_arch = 1 if $_ eq ':arch'; $do_collax = 1 if $_ eq ':collax'; $do_livebuild = 1 if $_ eq ':livebuild'; @@ -67,7 +69,7 @@ sub import { $do_apk = 1 if $_ eq ':apk'; $do_none = 1 if $_ eq ':none'; } - $do_rpm = $do_deb = $do_kiwi = $do_productcompose = $do_arch = $do_collax = $do_livebuild = $do_snapcraft = $do_appimage = $do_docker = $do_fissile = $do_helm = $do_flatpak = $do_mkosi = $do_apk = 1 if !$do_rpm && !$do_deb && !$do_kiwi && !$do_arch && !$do_collax && !$do_livebuild && !$do_snapcraft && !$do_appimage && !$do_docker && !$do_fissile && !$do_helm && !$do_flatpak && !$do_mkosi && !$do_productcompose && !$do_apk && !$do_none; + $do_rpm = $do_deb = $do_kiwi = $do_productcompose = $do_arch = $do_archsrcinfo = $do_collax = $do_livebuild = $do_snapcraft = $do_appimage = $do_docker = $do_fissile = $do_helm = $do_flatpak = $do_mkosi = $do_apk = 1 if !$do_rpm && !$do_deb && !$do_kiwi && !$do_arch && !$do_archsrcinfo && !$do_collax && !$do_livebuild && !$do_snapcraft && !$do_appimage && !$do_docker && !$do_fissile && !$do_helm && !$do_flatpak && !$do_mkosi && !$do_productcompose && !$do_apk && !$do_none; if ($do_deb) { require Build::Deb; @@ -75,6 +77,9 @@ sub import { if ($do_kiwi) { require Build::Kiwi; } + if ($do_archsrcinfo) { + require Build::Archsrcinfo; + } if ($do_arch) { require Build::Arch; } @@ -573,6 +578,7 @@ sub read_config { if (!$config->{'binarytype'}) { $config->{'binarytype'} = 'rpm' if $config->{'type'} eq 'spec'; $config->{'binarytype'} = 'deb' if $config->{'type'} eq 'dsc' || $config->{'type'} eq 'collax' || $config->{'type'} eq 'livebuild'; + $config->{'binarytype'} = 'arch' if $config->{'type'} eq 'archsrcinfo'; $config->{'binarytype'} = 'arch' if $config->{'type'} eq 'arch'; $config->{'binarytype'} = 'apk' if $config->{'type'} eq 'apk'; if (grep {$_ eq $config->{'type'}} qw{snapcraft appimage docker fissile kiwi mkosi}){ @@ -1273,6 +1279,7 @@ sub recipe2buildtype { return $1 if $recipe =~ /\.(spec|dsc|kiwi|productcompose|livebuild)$/; $recipe =~ s/.*\///; $recipe =~ s/^_service:.*://; + return 'archsrcinfo' if $recipe eq 'SRCINFO'; return 'arch' if $recipe eq 'PKGBUILD'; return 'apk' if $recipe eq 'APKBUILD'; return 'collax' if $recipe eq 'build.collax'; @@ -1346,6 +1353,7 @@ sub parse_typed { return Build::Docker::parse($cf, $fn, @args) if $do_docker && $buildtype eq 'docker'; return Build::Fissile::parse($cf, $fn, @args) if $do_fissile && $buildtype eq 'fissile'; return parse_simpleimage($cf, $fn, @args) if $buildtype eq 'simpleimage'; + return Build::Archsrcinfo::parse($cf, $fn, @args) if $do_archsrcinfo && $buildtype eq 'archsrcinfo'; return Build::Arch::parse($cf, $fn, @args) if $do_arch && $buildtype eq 'arch'; return Build::Apk::parse($cf, $fn, @args) if $do_apk && $buildtype eq 'apk'; return Build::Collax::parse($cf, $fn, @args) if $do_collax && $buildtype eq 'collax'; @@ -1368,6 +1376,8 @@ sub query { 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|\.zst)?$/; return Build::Arch::query($handle, %opts) if $do_arch && $binname =~ /\.arch$/; + return Build::Arch::query($handle, %opts) if $do_archsrcinfo && $binname =~ /\.pkg\.tar(?:\.gz|\.xz|\.zst)?$/; + return Build::Arch::query($handle, %opts) if $do_archsrcinfo && $binname =~ /\.arch$/; return Build::Apk::query($handle, %opts) if $do_arch && $binname =~ /\.apk$/; return undef; } @@ -1399,6 +1409,8 @@ sub queryhdrmd5 { return Build::Kiwi::queryhdrmd5(@_) if $do_kiwi && $binname =~ /\.raw.install$/; return Build::Arch::queryhdrmd5(@_) if $do_arch && $binname =~ /\.pkg\.tar(?:\.gz|\.xz|\.zst)?$/; return Build::Arch::queryhdrmd5(@_) if $do_arch && $binname =~ /\.arch$/; + return Build::Arch::queryhdrmd5(@_) if $do_archsrcinfo && $binname =~ /\.pkg\.tar(?:\.gz|\.xz|\.zst)?$/; + return Build::Arch::queryhdrmd5(@_) if $do_archsrcinfo && $binname =~ /\.arch$/; return Build::Apk::queryhdrmd5(@_) if $do_apk && $binname =~ /\.apk$/; return undef; } diff --git a/Build/Archsrcinfo.pm b/Build/Archsrcinfo.pm new file mode 100644 index 00000000..fba8bfdb --- /dev/null +++ b/Build/Archsrcinfo.pm @@ -0,0 +1,132 @@ +################################################################ +# +# Copyright (c) 1995-2014 SUSE Linux Products GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or 3 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING); if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# +################################################################ + +package Build::Archsrcinfo; + +use strict; +use warnings; + +sub _get_srcinfo_assets { + my ($vars, $asuf) = @_; + + my @sources = @{ $vars->{"source$asuf"} || [] }; + return () unless @sources; + + my @digests; + for my $digest_type ('sha512', 'sha384', 'sha256', 'sha224', 'sha1', 'md5') { + my $sums_key = "${digest_type}sums$asuf"; + if (exists $vars->{$sums_key} && @{$vars->{$sums_key}}) { + @digests = map { $_ eq 'SKIP' ? $_ : "$digest_type:$_" } @{ $vars->{$sums_key} }; + last; + } + } + + my @assets; + for my $i (0 .. $#sources) { + my $source_entry = $sources[$i]; + + # parse filename::URL formats + my $url = $source_entry; + if ($source_entry =~ /::/) { + ($url) = (split /::/, $source_entry, 2)[1]; + } + + # parse http/https/ftp protocols only + next unless $url =~ /^(https?|ftp):\/\//; + + my $asset = { 'url' => $url }; + my $digest = $digests[$i]; + + if ($digest && $digest ne 'SKIP') { + $asset->{'digest'} = $digest; + } + + push @assets, $asset; + } + + return @assets; +} + +sub parse { + my ($config, $srcinfo_file) = @_; + + my $ret = {}; + my $fh; + unless (open($fh, '<', $srcinfo_file)) { + $ret->{'error'} = "$srcinfo_file: $!"; + return $ret; + } + + my %vars; + while (my $line = <$fh>) { + chomp $line; + # newline / comment + next if $line =~ /^\s*(#.*)?$/; + + if ($line =~ /^\s*([^=\s]+)\s*=\s*(.*)\s*$/) { + my ($key, $value) = ($1, $2); + push @{ $vars{$key} }, $value; + } + } + close $fh; + + $ret->{'name'} = $vars{'pkgname'}->[0] if exists $vars{'pkgname'}; + if (exists $vars{'pkgver'} && exists $vars{'pkgrel'}) { + $ret->{'version'} = $vars{'pkgver'}->[0] . '-' . $vars{'pkgrel'}->[0]; + } elsif (exists $vars{'pkgver'}) { + $ret->{'version'} = $vars{'pkgver'}->[0]; + } + + $ret->{'deps'} = []; + my @dep_types = qw(depends makedepends checkdepends); + foreach my $dep_type (@dep_types) { + push @{ $ret->{'deps'} }, @{ $vars{$dep_type} || [] }; + } + + # i*86 + my ($arch) = Build::gettargetarchos($config); + $arch = 'i686' if $arch =~ /^i[345]86$/; + foreach my $dep_type (@dep_types) { + my $arch_dep_key = "${dep_type}_$arch"; + push @{ $ret->{'deps'} }, @{ $vars{$arch_dep_key} || [] }; + } + + # extract remote assets + $ret->{'remoteassets'} = []; + for my $asuf ('', "_$arch") { + my @assets = _get_srcinfo_assets(\%vars, $asuf); + push @{ $ret->{'remoteassets'} }, @assets if @assets; + } + + if (exists $vars{'arch'} && !grep { $_ eq 'any' } @{ $vars{'arch'} }) { + my %supported_arches = map { $_ => 1 } @{ $vars{'arch'} }; + + if (exists $supported_arches{'i686'}) { + $supported_arches{'i386'} = $supported_arches{'i486'} = $supported_arches{'i586'} = 1; + } + + $ret->{'exclarch'} = [ sort keys %supported_arches ]; + } + + return $ret; +} + + +1; diff --git a/Build/Repo.pm b/Build/Repo.pm index c6216b45..3fd5080f 100644 --- a/Build/Repo.pm +++ b/Build/Repo.pm @@ -25,6 +25,7 @@ use strict; our $do_rpmmd; our $do_deb; our $do_arch; +our $do_archsrcinfo; our $do_susetags; our $do_mdk; our $do_apk; @@ -33,12 +34,13 @@ sub import { for (@_) { $do_rpmmd = 1 if $_ eq ':rpmmd'; $do_deb = 1 if $_ eq ':deb'; + $do_archsrcinfo = 1 if $_ eq ':archsrcinfo'; $do_arch = 1 if $_ eq ':arch'; $do_susetags = 1 if $_ eq ':susetags'; $do_mdk = 1 if $_ eq ':mdk'; $do_apk = 1 if $_ eq ':apk'; } - $do_rpmmd = $do_deb = $do_arch = $do_susetags = $do_mdk = $do_apk = 1 unless $do_rpmmd || $do_deb || $do_arch || $do_susetags || $do_mdk || $do_apk; + $do_rpmmd = $do_deb = $do_arch = $do_archsrcinfo = $do_susetags = $do_mdk = $do_apk = 1 unless $do_rpmmd || $do_deb || $do_arch || $do_susetags || $do_mdk || $do_apk; if ($do_rpmmd) { require Build::Rpmmd; } @@ -51,6 +53,9 @@ sub import { if ($do_arch) { require Build::Archrepo; } + if ($do_archsrcinfo) { + require Build::Archrepo; + } if ($do_mdk) { require Build::Mdkrepo; } @@ -64,6 +69,7 @@ sub parse { return Build::Rpmmd::parse(@args) if $do_rpmmd && $type eq 'rpmmd'; return Build::Susetags::parse(@args) if $do_susetags && $type eq 'susetags'; return Build::Debrepo::parse(@args) if $do_deb && $type eq 'deb'; + return Build::Archrepo::parse(@args) if $do_archsrcinfo && $type eq 'archsrcinfo'; return Build::Archrepo::parse(@args) if $do_arch && $type eq 'arch'; return Build::Mdkrepo::parse(@args) if $do_arch && $type eq 'mdk'; return Build::Apkrepo::parse(@args) if $do_apk && $type eq 'apk'; diff --git a/PBuild/Job.pm b/PBuild/Job.pm index 70db5595..1329dc0e 100644 --- a/PBuild/Job.pm +++ b/PBuild/Job.pm @@ -125,6 +125,7 @@ sub collect_result { @d = map {"DEBS/$_"} sort(PBuild::Util::ls("$buildroot/.build.packages/DEBS")); # assume debbuild push @d, 'SDEBS'; } + @d = ('ARCHPKGS') if $p->{'recipe'} =~ /SRCINFO$/; @d = ('ARCHPKGS') if $p->{'recipe'} =~ /PKGBUILD$/; @d = ('APKS') if $p->{'recipe'} =~ /APKBUILD$/; @d = ('KIWI') if $p->{'recipe'} =~ /\.kiwi$/; diff --git a/PBuild/Recipe.pm b/PBuild/Recipe.pm index 650f0755..a0f527f8 100644 --- a/PBuild/Recipe.pm +++ b/PBuild/Recipe.pm @@ -42,6 +42,7 @@ sub find_recipe { return $files{'fissile.yml'} if $type eq 'fissile' && $files{'fissile.yml'}; return $files{'Chart.yaml'} if $type eq 'helm' && $files{'Chart.yaml'}; return (grep {/flatpak\.(?:ya?ml|json)$/} sort keys %files)[0] if $type eq 'flatpak'; + return $files{'SRCINFO'} if $type eq 'archsrcinfo' && $files{'SRCINFO'}; return $files{'PKGBUILD'} if $type eq 'arch' && $files{'PKGBUILD'}; return $files{'APKBUILD'} if $type eq 'apk' && $files{'APKBUILD'}; my $pkg = $p->{'pkg'}; @@ -219,6 +220,7 @@ sub looks_like_packagedir { return 1 if $file =~ /^(?:Dockerfile|mkosi)\./; return 1 if $file eq 'snapcraft.yaml' || $file eq 'appimage.yml'; return 1 if $file eq 'Dockerfile' || $file eq 'fissile.yml' || $file eq 'Chart.yml'; + return 1 if $file eq 'SRCINFO'; return 1 if $file eq 'PKGBUILD'; return 1 if $file eq 'APKBUILD'; } diff --git a/build-recipe b/build-recipe index 9e890963..b8d6183a 100644 --- a/build-recipe +++ b/build-recipe @@ -123,6 +123,7 @@ recipe_set_buildtype() { *.dsc) BUILDTYPE=dsc ;; *.kiwi) BUILDTYPE=kiwi ;; *.productcompose) BUILDTYPE=productcompose ;; + SRCINFO) BUILDTYPE=arch ;; PKGBUILD) BUILDTYPE=arch ;; APKBUILD) BUILDTYPE=apk ;; snapcraft.yaml) BUILDTYPE=snapcraft ;;