#!/usr/bin/env perl
# FIXME: add html output
use v5.34;
use warnings;
use feature 'signatures';
no warnings ('experimental::signatures');
use Getopt::Std ();
use JSON ();
use MIME::Base64 ();
use open IN => ':encoding(UTF-8)';
sub usage($fh) {
print $fh <<~'EOF'
Usage:
paku [-f FILE] FIXME
paku -h
EOF
}
sub help($fh) {
print $fh <<~'EOF'
Options:
-f FILE use data from FILE (default: paku.json)
-h, --help show this message
Generate package definitions for different package managers.
Examples:
FIXME:
$ FIXME
EOF
}
for (@ARGV) {
last if $_ eq '--';
if ($_ eq '--help') {
usage *STDOUT;
help *STDOUT;
exit;
}
}
my %opts;
if (!Getopt::Std::getopts('f:h', \%opts)) {
usage *STDERR;
exit 2;
}
if ($opts{h}) {
usage *STDOUT;
help *STDOUT;
exit;
}
my $dir = '.paku'; # FIXME: parameterize
my $fname = $opts{f} || 'paku.lock';
my $json_str = do {
open my $fh, $fname or die "Failed opening \"$fname\"";
local $/;
<$fh>;
};
my $json = JSON::decode_json($json_str);
my $action = $ARGV[0];
use Data::Dumper;
use POSIX qw();
use File::Basename ();
use Digest::MD5 ();
use Digest::SHA ();
# FIXME
my $base_url_prefix = 'https://euandre.org/s';
if ($action eq 'release') {
my $f = $ARGV[1];
my $name = File::Basename::basename $f;
my $size = (stat($f))[7];
my $now = `env LANG=POSIX.UTF-8 date -u '+%a, %d %b %Y %H:%M:%S +0000'`;
chomp $now;
my $fh;
open ($fh, '<', $f) or die "Can't open \"$f\": $!";
my $md5 = Digest::MD5->new->addfile($fh)->hexdigest;
close $fh;
open ($fh, '<', $f) or die "Can't open \"$f\": $!";
my $sha1 = Digest::SHA->new(1)->addfile($fh)->hexdigest;
close $fh;
open ($fh, '<', $f) or die "Can't open \"$f\": $!";
my $sha256 = Digest::SHA->new(256)->addfile($fh)->hexdigest;
close $fh;
open ($fh, '<', $f) or die "Can't open \"$f\": $!";
my $sha512 = Digest::SHA->new(512)->addfile($fh)->hexdigest;
close $fh;
print <<~EOF;
Date: $now
MD5Sum:
$md5 $size $name
SHA1:
$sha1 $size $name
SHA256:
$sha256 $size $name
SHA512:
$sha512 $size $name
EOF
exit;
}
if ($action eq 'nix') {
my $ns = 'org-euandre'; # FIXME
print <<~EOF;
{ pkgs }:
self: super: {
$ns = rec {
EOF
for my $pkg (@{$json->{packages}}) {
my $ver = $pkg->{version};
$ver =~ s/^v//;
$ver =~ s/\./-/g;
if ($pkg->{type} eq 'latest') {
$ver = 'latest';
print <<~EOF;
$pkg->{name} = $pkg->{name}-latest;
EOF
}
my $long = $pkg->{'long-description'};
$long =~ s/^(.)/ $1/gm;
my $base_url = $base_url_prefix . '/' . $pkg->{name};
print <<~EOF;
$pkg->{name}-$ver = pkgs.stdenv.mkDerivation rec {
name = "$pkg->{name}";
version = "$pkg->{version}";
src = fetchTarball {
url =
"$pkg->{url}";
sha256 = "$pkg->{sha256}";
};
nativeBuildInputs = with pkgs; [
EOF
for my $input (@{$pkg->{'native-inputs'}}) {
print <<~EOF;
$input
EOF
}
print <<~EOF;
];
buildInputs = with pkgs; [
EOF
for my $input (@{$pkg->{inputs}}) {
print <<~EOF;
$input
EOF
}
print <<~EOF;
];
makeFlags = [ "PREFIX=\$(out)" ];
doCheck = true;
enableParallelBuilding = true;
meta = with pkgs.lib; {
description = "$pkg->{description}";
longDescription = ''
$long
'';
homepage = "$base_url/";
changelog = "$base_url/CHANGELOG.html";
downloadPage = "$base_url/#releases";
license = licenses.agpl3;
platforms = platforms.unix;
};
};
EOF
# FIXME: escape quotes in description
# FIXME: parameterize license
}
print <<~EOF;
};
}
EOF
exit;
}
if ($action eq 'guix') {
my @pkgs = ();
my $ns = 'org euandre'; # FIXME
print <<~EOF;
(define-module ($ns packages)
EOF
for my $module (@{$json->{guix}{'module-use'}}) {
print <<~EOF;
#:use-module (gnu packages $module)
EOF
}
# FIXME: rename to 'licenses:'
print <<~EOF;
#:use-module ((guix licenses) #:prefix license:)
#:use-module (guix gexp)
#:use-module (guix packages)
#:use-module (guix download)
#:use-module (guix build-system gnu))
EOF
for my $pkg (@{$json->{packages}}) {
my $ver = $pkg->{version};
$ver =~ s/^v//;
$ver =~ s/\./-/g;
if ($pkg->{type} eq 'latest') {
push @pkgs, "$pkg->{name}";
$ver = 'latest';
}
my $long = $pkg->{'long-description'};
$long =~ s/^(.)/ $1/gm;
my $base_url = $base_url_prefix . '/' . $pkg->{name};
push @pkgs, "$pkg->{name}-$ver";
# FIXME: indentation of (source ...)
print <<~EOF;
(define-public $pkg->{name}-$ver
(package
(name "$pkg->{name}-$ver")
(version "$pkg->{version}")
(source (origin
(method url-fetch)
(uri "$pkg->{url}")
(sha256
(base32 "$pkg->{sha256FIXME}"))))
(build-system gnu-build-system)
(native-inputs
EOF
print ' (list';
for my $input (@{$pkg->{'native-inputs'}}) {
my $name = $json->{mappings}{$input}{guix} || $input;
print "\n $name";
}
print "))\n";
print " (inputs\n";
print ' (list';
for my $input (@{$pkg->{inputs}}) {
my $name = $json->{mappings}{$input}{guix} || $input;
print "\n $name";
}
print "))\n";
print <<~EOF;
(arguments
(list
#:make-flags '(list (string-append "PREFIX=" %output))
#:phases
#~(modify-phases %standard-phases
(delete 'configure))))
(synopsis "$pkg->{description}")
(description
"$pkg->{'long-description'}")
(home-page "$base_url/")
(license license:agpl3+)))
EOF
# FIXME: escape quotes in description
# FIXME: parameterize license
if ($pkg->{type} eq 'latest') {
print <<~EOF;
(define-public $pkg->{name}
(package
(inherit $pkg->{name}-latest)
(name "$pkg->{name}")))
EOF
}
}
print '(list';
for (@pkgs) {
print "\n $_";
}
print ")\n";
exit;
}
my %vars = (
tarballs => ["tarballs = \\\n"],
checkouts => ["checkouts = \\\n"],
destdirs => ["debian-destdirs = \\\n"],
ctrlfiles => ["control-files = \\\n"],
destdebs => ["destdir-debs = \\\n"],
debs => ["debs = \\\n"],
);
my @targets = ();
for my $pkg (@{$json->{packages}}) {
push @{$vars{tarballs}}, "\t$dir/tarballs/$pkg->{fname} \\\n";
push @{$vars{checkouts}}, "\t$dir/checkouts/$pkg->{name}-$pkg->{version} \\\n";
push @targets, <<~EOF;
$dir/tarballs/$pkg->{fname}:
mkdir -p \$(\@D)
wget -O \$\@ \\
'$pkg->{url}'
$dir/checkouts/$pkg->{name}-$pkg->{version}: $dir/tarballs/$pkg->{fname}
mkdir -p \$(\@D)
tar -C $dir/checkouts/ -xf $dir/tarballs/$pkg->{fname}
touch \$\@
EOF
next if $pkg->{architectures} ne 'any';
push @{$vars{destdirs}}, "\t$dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN \\\n";
push @{$vars{ctrlfiles}}, "\t$dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN/control \\\n";
push @{$vars{destdebs}}, "\t$dir/debian-destdir/$pkg->{name}-$pkg->{version}.deb \\\n";
push @{$vars{debs}}, "\t$dir/debian/$pkg->{name}_$pkg->{version}_all.deb \\\n";
my $ver = $pkg->{type} eq 'latest' ? '0.' . $pkg->{version} . '.latest' : $pkg->{version};
$ver =~ s/^v//;
my $maintainer_b64 = MIME::Base64::encode_base64 $pkg->{maintainer}, '';
my $desc_b64 = MIME::Base64::encode_base64 $pkg->{description}, '';
my $long_desc_b64 = MIME::Base64::encode_base64 $pkg->{'long-description'}, '';
push @targets, <<~EOF;
$dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN: $dir/checkouts/$pkg->{name}-$pkg->{version}
\$(MAKE) \\
-C $dir/checkouts/$pkg->{name}-$pkg->{version} \\
install \\
PREFIX=/usr \\
DESTDIR="\$\$PWD"/$dir/debian-destdir/$pkg->{name}-$pkg->{version}
mkdir -p \$\@
touch \$\@
$dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN/control: $dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN
printf '' > \$\@
printf 'Package: $pkg->{name}\\n' >> \$\@
printf 'Version: $ver\\n' >> \$\@
printf 'Section: custom\\n' >> \$\@
printf 'Depends:\\n' >> \$\@
printf 'Priority: optional\\n' >> \$\@
printf 'Architecture: all\\n' >> \$\@
printf 'Essential: no\\n' >> \$\@
printf 'Maintainer: ' >> \$\@
printf '$maintainer_b64' | base64 -d >> \$\@
printf '\\n' >> \$\@
printf 'Description: ' >> \$\@
printf '$desc_b64' | base64 -d >> \$\@
printf '\\n' >> \$\@
printf '$long_desc_b64' | \\
base64 -d | \\
sed 's|^\$\$|.|' | \\
sed 's|^| |' >> \$\@
printf '\\n' >> \$\@
$dir/debian-destdir/$pkg->{name}-$pkg->{version}.deb: $dir/debian-destdir/$pkg->{name}-$pkg->{version}/DEBIAN/control
dpkg-deb --build $dir/debian-destdir/$pkg->{name}-$pkg->{version}
$dir/debian/$pkg->{name}_$pkg->{version}_all.deb: $dir/debian-destdir/$pkg->{name}-$pkg->{version}.deb
mkdir -p \$(\@D)
cp $dir/debian-destdir/$pkg->{name}-$pkg->{version}.deb \$\@
EOF
}
print
@{$vars{tarballs}}, "\n",
@{$vars{checkouts}}, "\n",
@{$vars{destdirs}}, "\n",
@{$vars{ctrlfiles}}, "\n",
@{$vars{destdebs}}, "\n",
@{$vars{debs}}, "\n";
print <<~EOF;
FIXME = eu\@euandre.org
all: $dir/debian/InRelease $dir/debian/Release.gpg $dir/debian/public-key.asc
public-dir:
\@printf '$dir/debian'
$dir/debian/Packages: \$(debs)
cd \$(\@D) && dpkg-scanpackages -m . > \$(\@F)
$dir/debian/Release: $dir/debian/Packages
perl src/bin/paku release $dir/debian/Packages > \$\@
$dir/debian/Release.gpg: $dir/debian/Release
gpg -abs -o \$\@ $dir/debian/Release
$dir/debian/InRelease: $dir/debian/Release
gpg --default-key \$(FIXME) -a --clear-sign -o \$\@ $dir/debian/Release
$dir/debian/public-key.asc: $dir/debian/Release
gpg --armour --export \$(FIXME) > \$\@
EOF
print @targets;