#!/usr/bin/perl # apk add perl-libwww perl-json use strict; use warnings; use 5.016; use LWP::UserAgent; use LWP::ConnCache; use CPAN::Meta; use Module::CoreList; use JSON; use Text::Wrap qw(wrap $columns); use List::Util qw (uniq); use File::Basename qw(basename dirname); use URI; my $license_mappings = { "perl_5" => "GPL-1.0-or-later OR Artistic-1.0-Perl", "artistic_2" => "Artistic-2.0", "gpl_2" => "GPL-2.0-only", "gpl_3" => "GPL-3.0-only", "mit" => "MIT", "apache_2_0" => "Apache-2.0", }; my $package_mappings = { "LWP" => "perl-libwww", "libwww-perl" => "perl-libwww", "TermReadKey" => "perl-term-readkey", "perl-ldap" => "perl-ldap", "PerlIO-gzip" => "perl-io-gzip", "ControlX10-CM11" => "perl-control-x10", "File-RsyncP" => "perl-file-rsync", "Perl-Critic" => "perl-critic", "Perl-Tidy" => "perl-tidy", "ack" => "ack", "Mail-SpamAssassin" => "spamassassin", }; our $packager = ""; my $template = <<'EOF'; # Automatically generated by apkbuild-cpan, template 4 [% authors %] pkgname=[% pkgname %] pkgver=[% pkgver %] pkgrel=[% pkgrel %] #_pkgreal is used by apkbuild-cpan to find modules at MetaCpan _pkgreal=[% pkgreal %] pkgdesc="Perl module for [% pkgreal %]" provides="[% provides %]" replaces="[% replaces %]" url="https://metacpan.org/release/[% pkgreal %]/" arch="noarch" options="[% options %]"[% options_comment %] license="GPL-1.0-or-later OR Artistic-1.0-Perl" depends="perl" makedepends="" checkdepends="" subpackages="$pkgname-doc" source="[% source %]" builddir="[% builddir %]" build() { : } check() { : } package() { : } EOF our $ua = LWP::UserAgent->new(); our $json = JSON->new; $ua->env_proxy; $ua->conn_cache( LWP::ConnCache->new() ); sub read_file { my ($filename) = @_; local $/; open my $fh, "<", $filename or die "could not open $filename: $!"; return <$fh>; } sub read_assignments_from_file { my ($filename) = @_; return () if ( !-e $filename ); my $text = read_file($filename); my %sline = $text =~ /^(\w+)\s*=\s*([^\"\n]*)$/mg; my %mline = $text =~ /^(\w+)\s*=\s*\"([^\"]*)\"$/mg; my %hash = ( %sline, %mline ); return \%hash if $filename ne 'APKBUILD'; my $authors = join( "\n", $text =~ /^# Contributor: .*$/mg, $text =~ /^# Maintainer: .*$/mg ); $hash{'authors'} = $authors if length($authors) > 1; my $options = $text =~ m/^options=\"(.*)\"(.*)$/mg; $hash{'options'} = "$1" if length($options) >= 1; $hash{'options_comment'} = "$2" if length($options) >= 1; return \%hash; } sub map_cpan_to_apk { my ($cpan_distrib) = @_; return $package_mappings->{$cpan_distrib} if exists $package_mappings->{$cpan_distrib}; # most packages are named according to the # distribution name return 'perl-' . lc $cpan_distrib; } sub read_apkbuild { return read_assignments_from_file("APKBUILD"); } sub format_line { my $line = shift; return "\t" . $line . "\n"; } sub format_source { my $srcurl = shift; my $orig_src = shift; $orig_src =~ s/^\n//mg; my @sources = split (/\n/m, $orig_src); return $srcurl if scalar @sources le 1; my $source = format_line($srcurl); shift @sources; my $patches = undef; for my $patch (@sources) { $patch =~ s/\t//g; next if $patch eq ""; $patches .= format_line($patch); } return "\n" . $source . (defined $patches ? $patches : '') . "\t"; } sub write_apkbuild { my ( $distdata, $apkbuild, $moddata ) = @_; my $authors = undef; my $replaces = undef; my $provides = undef; my $pkgrel = 0; my $options = undef; my $options_comment = undef; my $orig_source = undef; if ( defined $apkbuild ) { $authors = $apkbuild->{authors}; $provides = $apkbuild->{provides}; $replaces = $apkbuild->{replaces}; $pkgrel = $apkbuild->{pkgrel}; $options = $apkbuild->{options} if defined $apkbuild->{options}; $options_comment = $apkbuild->{options_comment} if defined $apkbuild->{options_comment}; $orig_source = $apkbuild->{source}; if ( $apkbuild->{pkgver} eq $moddata->{version} ) { $pkgrel++; } } my $builddir; my $srcurl = $moddata->{download_url}; my $filename = (URI->new($srcurl)->path_segments)[-1]; my $pkgreal = $moddata->{distribution}; my $pkgver = $moddata->{version} =~ s/^[^0-9]+//r; if ( $filename =~ m/$pkgreal-$pkgver/ ) { $builddir = "\$srcdir/\$_pkgreal-\$pkgver"; } elsif ( $filename =~ m/$pkgreal-v${pkgver}/ ) { $builddir = "\$srcdir/\$_pkgreal-v\$pkgver"; } else { $builddir = "\$srcdir/\$_pkgreal-" . $moddata->{version}; } my %repl = ( authors => ( $authors or "# Contributor: $packager\n# Maintainer: $packager" ), pkgname => map_cpan_to_apk( $moddata->{distribution} ), pkgreal => $pkgreal, pkgver => $pkgver, pkgrel => $pkgrel, source => format_source($srcurl, $orig_source), pkgdesc => $distdata->{abstract}, provides => ( $provides or '' ), replaces => ( $replaces or '' ), builddir => $builddir, options => ( $options or '' ), options_comment => ( $options_comment or '' ), ); $repl{source} =~ s/$repl{pkgver}/\$pkgver/g; $template =~ s/\[% (.*?) %\]/$repl{$1}/g; open my $fh, '>', "APKBUILD" or die; print $fh $template; close $fh; say "Wrote $repl{pkgname}/APKBUILD"; } sub query_metacpan { my ($type, $name) = @_; my $response = $ua->get("https://fastapi.metacpan.org/$type/$name"); $response->is_success or die $response->status_line . " unable to find $name, verify Perl Module name in APKBUILD\n"; my $data = $json->decode( $response->decoded_content ); $data->{error} and die "Error trying to locate $name: $data->{error}\n"; return $data; } sub parse_deps { my ($reqs) = @_; my $distfiles = {}; my $deps = ""; my $reqmodlist = ""; #Get list of unique required modules from all parameters foreach $reqs (@_) { for my $module ( $reqs->required_modules ) { $reqmodlist .= "$module " unless $reqmodlist =~ m/^$module$/; } } # Check add the require module to $dep if not part of core perl foreach my $module ( split " ", $reqmodlist ) { next if $module eq 'perl'; if ( Module::CoreList->is_core($module) ) { my $perlver = Module::CoreList->first_release($module); say " $module is part of core perl since $perlver."; next; } # map module name to package name my $moddata = query_metacpan( module => $module ); $distfiles->{$module} = $moddata->{distribution}; } # map package names to alpine packages foreach my $module ( sort keys %{$distfiles} ) { my $pkgname = map_cpan_to_apk( $distfiles->{$module} ); $deps .= "$pkgname " unless $deps =~ m/\b$pkgname\b/; } $deps =~ s/\h+/ /g; $deps =~ s/ $//; return defined $deps ? $deps : ''; } sub prepare_tree { system("abuild checksum unpack prepare") == 0 or die "abuild checksum failed"; } sub update_functions { my $apkbuild = read_apkbuild; my $pkgver = $apkbuild->{'pkgver'}; my $build_pl_found = glob("src/*${pkgver}/Build.PL"); my $build_func; my $check_func; my $package_func; my $text = read_file "APKBUILD"; if ( $build_pl_found ) { $build_func = <<'EOF'; build() { export CFLAGS=$(perl -MConfig -E 'say $Config{ccflags}') perl Build.PL \ --installdirs=vendor \ --create_packlist=0 ./Build } EOF $package_func = <<'EOF'; package() { ./Build install --destdir="$pkgdir" } EOF $check_func = <<'EOF'; check() { ./Build test } EOF } else { $build_func = <<'EOF'; build() { export CFLAGS=$(perl -MConfig -E 'say $Config{ccflags}') PERL_MM_USE_DEFAULT=1 perl -I. Makefile.PL \ INSTALLDIRS=vendor \ NO_PACKLIST=1 \ NO_PERLLOCAL=1 make } EOF $package_func = <<'EOF'; package() { make DESTDIR="$pkgdir" install } EOF $check_func = <<'EOF'; check() { export CFLAGS=$(perl -MConfig -E 'say $Config{ccflags}') make test } EOF } if ( length(`find src/*$pkgver/lib -name '*.pod'`) ) { $package_func .= <<'EOF'; doc() { local file; find "$pkgdir" -name "*.pod" | while read -r file; do amove "${file#"$pkgdir"}" done default_doc } EOF } $text =~ s/^build\(\) \{.*?^\}\n/$build_func/smg or die "Can't replace build function APKBUILD"; $text =~ s/^package\(\) \{.*?^\}\n/$package_func/smg or die "Can't replace package function APKBUILD"; $text =~ s/^check\(\) \{.*?^\}\n/$check_func/smg or die "Can't replace check function APKBUILD"; open my $fh, '>', "APKBUILD" or die; print $fh $text; close $fh; } sub sort_pkgs_by_orig { my $sort_order = shift; my $list = shift; return '' if !defined $list; my @array_to_sort = split / /, $list; s/[\t\n\r\f]/ /g for $sort_order; # remove embedded new lines from sort order s/\h+/ /g for $sort_order; # remove embedded spaces from sort order my @sort_order = split / /, $sort_order; #get order of order by string my $count = 0; my %position_of; $position_of{$_} = $count++ for @sort_order; my %sorted; # holds the position of array to sort foreach (@array_to_sort) { my $pos = $position_of{$_}; if ( !defined $pos ) { # not found in sort order so add to the end $sorted{$_} = $count; $count++; } else { $sorted{$_} = $pos; } } my @tmp; # hold array of sorted in position order foreach my $name ( sort { $sorted{$a} <=> $sorted{$b} } keys %sorted ) { push @tmp, $name; } # remove any duplicates uniq @tmp; return join( " ", @tmp ); } sub format_depends { my $deps = shift; return '' if !defined $deps; $columns = 102; $deps =~ s/ {2,}/ /g; $deps =~ s/^\s//g; $deps =~ s/\s$//g; if ( length($deps) >= $columns ) { $deps = wrap( "\t", "\t", $deps ); } $deps =~ s/\s$//g; if ( length($deps) >= $columns ) { $deps = "\n" . $deps . "\n\t"; } return $deps; } sub do_depends { my $modver = shift; my $oldapkbuild = shift; my $apkbuild = read_apkbuild; my $metaprefix = ''; if ( exists $apkbuild->{'_realname'} ) { $metaprefix = "src/" . $apkbuild->{'_realname'}; } elsif ( exists $apkbuild->{'_pkgreal'} ) { $metaprefix = "src/" . $apkbuild->{'_pkgreal'}; } elsif ( exists $apkbuild->{'_pkgname'} ) { $metaprefix = "src/" . $apkbuild->{'_pkgname'}; } elsif ( exists $apkbuild->{'_name'} ) { $metaprefix = "src/" . $apkbuild->{'_name'}; } elsif ( exists $apkbuild->{'_realpkgname'} ) { $metaprefix = "src/" . $apkbuild->{'_realpkgname'}; } elsif ( exists $apkbuild->{'_pkg_real'} ) { $metaprefix = "src/" . $apkbuild->{'_pkg_real'}; } else { die "Unable to find meta file directory - check APKBUILD Perl Module Name"; } $modver = defined($modver) ? $modver : $apkbuild->{'pkgver'}; $modver =~ s/v//g; # try with partial metaprefix my @metafiles = glob(" $metaprefix-*${modver}/*META.json $metaprefix-*${modver}/*META.yml "); my $build_path = glob(" src/*${modver}/Build.PL src/*${modver}/Makefile.PL "); if ( @metafiles ) { $metaprefix = dirname($metafiles[0]); } elsif ( $build_path ) { $metaprefix = dirname($build_path); my $build_file = basename($build_path); system("cd $metaprefix && perl -I. $build_file"); # try again with full metaprefix push @metafiles, glob(" $metaprefix/*META.json $metaprefix/*META.yml "); } else { die "Unable to find meta, makefile, and build.pl - cannot proceed" } die "No meta files found after executing $build_path" unless @metafiles; my $builddir = do { my $pkgreal = $apkbuild->{'_pkgreal'}; my $oldbuilddir = $apkbuild->{builddir}; my $newbuilddir = $metaprefix; $newbuilddir =~ s/src/\$srcdir/; $newbuilddir =~ s/$pkgreal/\$_pkgreal/; $newbuilddir =~ s/$modver/\$pkgver/; $newbuilddir eq $oldbuilddir ? undef : $newbuilddir }; my $meta; foreach my $metafile ( @metafiles ) { if ( -e "$metafile" ) { say "Using meta information from $metafile"; $meta = CPAN::Meta->load_file("$metafile"); last; } } my $deps; my $abstract; my $license; if ($meta) { $license = join " OR ", map { $license_mappings->{$_} or $_ } $meta->license; say "License: $license"; $abstract = $meta->abstract; say "Abstract: $abstract"; $deps = parse_deps( $meta->effective_prereqs->requirements_for( 'runtime', 'requires' ) ); } if ( defined $oldapkbuild->{'depends'} ) { if ( defined $oldapkbuild->{'cpandepends'} ) { $oldapkbuild->{'depends'} =~ s/\$cpandepends/$oldapkbuild->{'cpandepends'}/g; } my @libs = split /\s+/, $oldapkbuild->{'depends'}; if ( @libs ) { my $libs = join ' ', grep { not (m/perl\-\w+[-\w+]+/ or ($_ eq 'perl')) } @libs; $deps .= " " . $libs if defined $deps; } $deps = sort_pkgs_by_orig( $oldapkbuild->{'depends'}, $deps ); } if ( defined $deps && $deps eq '' ) { if (defined $oldapkbuild->{'depends'} && ! $meta ) { $deps = $oldapkbuild->{'depends'}; } else { $deps = "perl"; } } else { $deps = format_depends( "perl " . $deps ) if defined $deps; } my $makedeps = ''; if ($meta) { say "CPAN runtime Required: $deps"; say "CPAN runtime Recommended: " . parse_deps( $meta->effective_prereqs->requirements_for( 'runtime', 'recommends' ) ); $makedeps = parse_deps( $meta->effective_prereqs->requirements_for( 'configure', 'requires' ), $meta->effective_prereqs->requirements_for( 'configure', 'recommends' ), $meta->effective_prereqs->requirements_for( 'build', 'requires' ), $meta->effective_prereqs->requirements_for( 'build', 'recommends' ) ); if ( -e "$metaprefix/Build.PL" ) { $makedeps = "$makedeps perl-module-build" unless $makedeps =~ m/^perl-module-build$/; } } if ( defined $oldapkbuild->{'makedepends'} ) { if ( defined $oldapkbuild->{'cpanmakedepends'} ) { $oldapkbuild->{'makedepends'} =~ s/\$cpanmakedepends/$oldapkbuild->{'cpanmakedepends'}/g; } my @libs = split /\s+/, $oldapkbuild->{'makedepends'}; if ( @libs ) { my $libs = join ' ', grep { not (m/perl\-\w+[-\w+]+/) } @libs; $makedeps .= " " . $libs; } $makedeps = sort_pkgs_by_orig( $oldapkbuild->{'makedepends'}, $makedeps ); } my $has_xs = length(`find $metaprefix -name '*.xs'`); my $perldev = $has_xs ? "perl-dev" : ''; if ( defined $makedeps && $makedeps eq '' ) { if (defined $oldapkbuild->{'makedepends'} && ! $meta) { $makedeps = $oldapkbuild->{'makedepends'}; my @tmp = split(" ", $makedeps); @tmp = grep ! /$perldev/, @tmp if ! $has_xs; $makedeps = format_depends(join(" ", @tmp)); } else { $makedeps = $perldev; } } else { $makedeps = format_depends( $perldev . " " . $makedeps ); } $makedeps =~ s/ +$//g; my $checkdeps; if ($meta) { say "CPAN build deps: $makedeps"; say " CPAN build requires: " . parse_deps( $meta->effective_prereqs->requirements_for( 'configure', 'requires' ), $meta->effective_prereqs->requirements_for( 'build', 'requires' ) ); say " CPAN build recommends: " . parse_deps( $meta->effective_prereqs->requirements_for( 'configure', 'recommends' ), $meta->effective_prereqs->requirements_for( 'build', 'recommends' ) ); $checkdeps = parse_deps( $meta->effective_prereqs->requirements_for( 'test', 'requires' ), $meta->effective_prereqs->requirements_for( 'test', 'recommends' ) ); } if ( defined $oldapkbuild->{'checkdepends'} ) { if ( defined $oldapkbuild->{'cpancheckdepends'} ) { $oldapkbuild->{'checkdepends'} =~ s/\$cpancheckdepends/$oldapkbuild->{'cpancheckdepends'}/g; } my @libs = split /\s+/, $oldapkbuild->{'checkdepends'}; if ( @libs ) { my $libs = join ' ', grep { not (m/perl\-\w+[-\w+]+/) } @libs; $checkdeps .= " " . $libs; } $checkdeps = sort_pkgs_by_orig( $oldapkbuild->{'checkdepends'}, $checkdeps ); } if ( defined $checkdeps && $checkdeps eq '' ) { if (defined $oldapkbuild->{'checkdeps'} && ! $meta) { $checkdeps = $oldapkbuild->{'checkdeps'}; } } else { $checkdeps = format_depends($checkdeps); } say "CPAN check deps: $checkdeps"; my $text = read_file "APKBUILD"; if ( $abstract && $abstract ne 'unknown' ) { $abstract =~ s/"/\\"/g; $abstract =~ s/[.\s]+$//; $text =~ s/^pkgdesc=\"([^\"]*)\"$/pkgdesc=\"$abstract\"/mg or die "Can't find pkgdesc line in APKBUILD"; } if ( exists $oldapkbuild->{'arch'} ) { # Prevent removal of empty arch variable for disabled aports my $subst_arch = $oldapkbuild->{'arch'} ? '"' . $oldapkbuild->{'arch'} . '"' : ""; $text =~ s/^arch=\"([^\"]*)\"$/arch=$subst_arch/mg or die "Can't find arch line in APKBUILD"; } elsif ( $has_xs ) { $text =~ s/^arch=\"([^\"]*)\"$/arch="all"/mg or die "Can't find arch line in APKBUILD"; } if ( defined $license && $license ne 'unknown' ) { $text =~ s/^license=\"([^\"]*)\"$/license=\"$license\"/mg or die "Can't find license line in APKBUILD"; } $text =~ s/^depends=\"([^\"]*)\"$/depends=\"$deps\"/mg or $text =~ s/(license=.*\n)/$1depends=\"$deps\"\n/gm or die "Can't insert depends line in APKBUILD" if defined $deps; $text =~ s/^makedepends=\"([^\"]*)\"$/makedepends=\"$makedeps\"/mg or $text =~ s/(depends=.*\n)/$1makedepends=\"$makedeps\"\n/gm or die "Can't insert makedepends line in APKBUILD" if defined $makedeps; $text =~ s/^checkdepends=\"([^\"]*)\"$/checkdepends=\"$checkdeps\"/mg or $text =~ s/(makedepends=.*\n)/$1checkdepends=\"$checkdeps\"\n/gm or die "Can't insert checkdepends line in APKBUILD" if defined $checkdeps; if (defined $builddir) { print "\n\$builddir redefined:\n\tOLD: '", $apkbuild->{builddir}, "', NEW: '", $builddir, "'\n"; $text =~ s/^builddir=\"([^\"]*)\"/builddir="$builddir"/mg; } # remove empty variables $text =~ s/.*="".{0,}\n//g; open my $fh, '>', "APKBUILD" or die; print $fh $text; close $fh; } sub get_data { my $apkbuild = read_apkbuild; my $pkgreal = ''; if ( exists $apkbuild->{_realname} ) { $pkgreal = $apkbuild->{_realname}; } elsif ( exists $apkbuild->{_pkgreal} ) { $pkgreal = $apkbuild->{_pkgreal}; } elsif ( exists $apkbuild->{_pkgname} ) { $pkgreal = $apkbuild->{_pkgname}; } elsif ( exists $apkbuild->{_name} ) { $pkgreal = $apkbuild->{_name}; } elsif ( exists $apkbuild->{_realpkgname} ) { $pkgreal = $apkbuild->{_realpkgname}; } elsif ( exists $apkbuild->{_pkg_real} ) { $pkgreal = $apkbuild->{_pkg_real}; } else { my $module = ''; my $distribution = ''; while ( ( my $key, my $value ) = each(%$apkbuild) ) { # Do not parse any depends lines to not find incorrect module if ( $key =~ m/.*depends.*/ ) { next; } # Try to find a perl module name in APKBUILD if ( $value =~ m/((\w+::)+\w+)/g ) { # Match Perl Module names containing :: $module .= "$1 " unless $module =~ m/$1/; } elsif ( $value =~ m/(([A-Z]\w+-)+\w+)/ ) { # Match possible distribution names with - $distribution .= "$1 " unless $distribution =~ m/ *$1 /; } elsif ( $value =~ m/.*release\/([A-Z]\w+).*/ ) { # Match Single Word Perl Module Name after release in URL? $distribution .= "$1 " unless $distribution =~ m/ *$1 /; } } # Want to try the traditional Module::Name first my $list = $module . $distribution; foreach ( split / /, $list ) { my $type = ''; if ( $_ =~ m/::/ ) { $type = 'module'; } else { $type = 'release'; } my $moddata; eval { $moddata = query_metacpan( $type => $_ ); }; next if $@ or ref $moddata ne 'HASH' or not $moddata->{distribution}; $pkgreal = $moddata->{distribution}; last; } } $pkgreal =~ s/-\$pkgver//g; my $distdata = query_metacpan( release => $pkgreal ); my $moddata = query_metacpan( module => $distdata->{main_module} ); return ( $apkbuild, $distdata, $moddata ); } my $abuild_conf = read_assignments_from_file("/etc/abuild.conf"); $packager = $abuild_conf->{PACKAGER} if $abuild_conf->{PACKAGER}; my $user_abuild_conf = read_assignments_from_file( $ENV{"HOME"} . "/.abuild/abuild.conf" ); $packager = $user_abuild_conf->{PACKAGER} if $user_abuild_conf->{PACKAGER}; my $command = $ARGV[0] // ''; if ( $command eq "create" ) { my $module = $ARGV[1]; $module or die "Module name is a mandatory argument"; my $moddata = query_metacpan( module => $module ); my $distdata = query_metacpan( release => $moddata->{distribution} ); my $apkname = map_cpan_to_apk $distdata->{metadata}{name}; mkdir $apkname; chdir $apkname; write_apkbuild( $distdata, undef, $moddata ); prepare_tree; update_functions; do_depends $moddata->{version}; } elsif ( $command eq "recreate" ) { #TODO: likely should keep pkgrel the same on recreate my ( $apkbuild, $distdata, $moddata ) = get_data; my $pkgver = $moddata->{version} =~ s/^[^0-9]+//r; if ( $pkgver ne $apkbuild->{pkgver} ) { #Reset pkgrel on upgrade on recreate say "Upgrading CPAN module from $apkbuild->{pkgver} to $pkgver"; $apkbuild->{pkgrel} = 0; } write_apkbuild( $distdata, $apkbuild, $moddata ); prepare_tree; update_functions; do_depends( $moddata->{version}, $apkbuild ); } elsif ( $command eq "upgrade" ) { my ( $apkbuild, $distdata, $moddata ) = get_data; my $pkgver = $moddata->{version} =~ s/^[^0-9]+//r; if ( $pkgver ne $apkbuild->{pkgver} ) { say "Upgrading CPAN module from $apkbuild->{pkgver} to $pkgver"; my $text = read_file "APKBUILD"; $text =~ s/^pkgver=(.*)$/pkgver=$pkgver/mg or die "Can't find pkgver line in APKBUILD"; $text =~ s/^pkgrel=(.*)$/pkgrel=0/mg; #FIXME: review whether this works over time # It deletes the blank line before the checksum # So prepare_tree does not insert extra blank line $text =~ s/\n^(.*sums=.*\n)/$1/mg; open my $fh, '>', "APKBUILD" or die; say $fh $text; close $fh; prepare_tree; do_depends( $moddata->{version}, $apkbuild ); } else { say "Up-to-date with CPAN"; } } elsif ( $command eq "check" ) { my ( $apkbuild, $distdata, $moddata ) = get_data; my $pkgver = $moddata->{version} =~ s/^[^0-9]+//r; say "$apkbuild->{pkgname}: Latest version: $pkgver Packaged version: $apkbuild->{pkgver}"; if ( $pkgver ne $apkbuild->{pkgver} ) { exit(1); } } elsif ( $command eq "update" ) { prepare_tree; do_depends; } else { say "Usage: apkbuild-cpan [create | check | recreate | update | upgrade]"; exit; }