#!/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 $package (@{$json->{packages}}) {
for my $version (@{$package->{versions}}) {
my $ver = $version->{name};
$ver =~ s/^v//;
$ver =~ s/\./-/g;
if ($version->{type} eq 'latest') {
$ver = 'latest';
print <<~EOF;
$package->{name} = $package->{name}-latest;
EOF
}
my $long = $version->{'long-description'};
$long =~ s/^(.)/ $1/gm;
my $base_url = $base_url_prefix . '/' . $package->{name};
print <<~EOF;
$package->{name}-$ver = pkgs.stdenv.mkDerivation rec {
name = "$package->{name}";
version = "$version->{name}";
src = fetchTarball {
url =
"$version->{url}";
sha256 = "$version->{sha256}";
};
nativeBuildInputs = with pkgs; [
EOF
for my $input (@{$version->{'native-inputs'}}) {
print <<~EOF;
$input
EOF
}
print <<~EOF;
];
buildInputs = with pkgs; [
EOF
for my $input (@{$version->{inputs}}) {
print <<~EOF;
$input
EOF
}
print <<~EOF;
];
makeFlags = [ "PREFIX=\$(out)" ];
doCheck = true;
enableParallelBuilding = true;
meta = with pkgs.lib; {
description = "$version->{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 $package (@{$json->{packages}}) {
for my $version (@{$package->{versions}}) {
my $ver = $version->{name};
$ver =~ s/^v//;
$ver =~ s/\./-/g;
if ($version->{type} eq 'latest') {
push @pkgs, "$package->{name}";
$ver = 'latest';
}
my $long = $version->{'long-description'};
$long =~ s/^(.)/ $1/gm;
my $base_url = $base_url_prefix . '/' . $package->{name};
push @pkgs, "$package->{name}-$ver";
# FIXME: indentation of (source ...)
print <<~EOF;
(define-public $package->{name}-$ver
(package
(name "$package->{name}-$ver")
(version "$version->{name}")
(source (origin
(method url-fetch)
(uri "$version->{url}")
(sha256
(base32 "$version->{sha256FIXME}"))))
(build-system gnu-build-system)
(native-inputs
EOF
print ' (list';
for my $input (@{$version->{'native-inputs'}}) {
my $name = $json->{mappings}{$input}{guix} || $input;
print "\n $name";
}
print "))\n";
print " (inputs\n";
print ' (list';
for my $input (@{$version->{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 "$version->{description}")
(description
"$version->{'long-description'}")
(home-page "$base_url/")
(license license:agpl3+)))
EOF
# FIXME: escape quotes in description
# FIXME: parameterize license
if ($version->{type} eq 'latest') {
print <<~EOF;
(define-public $package->{name}
(package
(inherit $package->{name}-latest)
(name "$package->{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 $package (@{$json->{packages}}) {
for my $version (@{$package->{versions}}) {
push @{$vars{tarballs}}, "\t$dir/tarballs/$version->{fname} \\\n";
push @{$vars{checkouts}}, "\t$dir/checkouts/$package->{name}-$version->{name} \\\n";
push @targets, <<~EOF;
$dir/tarballs/$version->{fname}:
mkdir -p \$(\@D)
wget -O \$\@ \\
'$version->{url}'
$dir/checkouts/$package->{name}-$version->{name}: $dir/tarballs/$version->{fname}
mkdir -p \$(\@D)
tar -C $dir/checkouts/ -xf $dir/tarballs/$version->{fname}
touch \$\@
EOF
next if $version->{architectures} ne 'any';
push @{$vars{destdirs}}, "\t$dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN \\\n";
push @{$vars{ctrlfiles}}, "\t$dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN/control \\\n";
push @{$vars{destdebs}}, "\t$dir/debian-destdir/$package->{name}-$version->{name}.deb \\\n";
push @{$vars{debs}}, "\t$dir/debian/$package->{name}_$version->{name}_all.deb \\\n";
my $ver = $version->{type} eq 'latest' ? '0.' . $version->{name} . '.latest' : $version->{name};
$ver =~ s/^v//;
my $maintainer_b64 = MIME::Base64::encode_base64 $version->{maintainer}, '';
my $desc_b64 = MIME::Base64::encode_base64 $version->{description}, '';
my $long_desc_b64 = MIME::Base64::encode_base64 $version->{'long-description'}, '';
push @targets, <<~EOF;
$dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN: $dir/checkouts/$package->{name}-$version->{name}
\$(MAKE) \\
-C $dir/checkouts/$package->{name}-$version->{name} \\
install \\
PREFIX=/usr \\
DESTDIR="\$\$PWD"/$dir/debian-destdir/$package->{name}-$version->{name}
mkdir -p \$\@
touch \$\@
$dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN/control: $dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN
printf '' > \$\@
printf 'Package: $package->{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/$package->{name}-$version->{name}.deb: $dir/debian-destdir/$package->{name}-$version->{name}/DEBIAN/control
dpkg-deb --build $dir/debian-destdir/$package->{name}-$version->{name}
$dir/debian/$package->{name}_$version->{name}_all.deb: $dir/debian-destdir/$package->{name}-$version->{name}.deb
mkdir -p \$(\@D)
cp $dir/debian-destdir/$package->{name}-$version->{name}.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;