Add makepkg-template

This allows for somewhat easy templating for PKGBUILDs.

Signed-off-by: Florian Pritz <bluewind@xinu.at>
Signed-off-by: Allan McRae <allan@archlinux.org>
This commit is contained in:
Florian Pritz 2013-05-01 00:18:17 +02:00 committed by Allan McRae
parent 7ea58d09f6
commit ce3125196d
7 changed files with 324 additions and 0 deletions

View File

@ -97,6 +97,11 @@ AC_ARG_WITH(buildscript,
AS_HELP_STRING([--with-buildscript=name], [set the build script name used by makepkg]),
[BUILDSCRIPT=$withval], [BUILDSCRIPT=PKGBUILD])
# Help line for buildscript filename
AC_ARG_WITH(makepkg-template-dir,
AS_HELP_STRING([--with-makepkg-template-dir=name], [set the template dir used by makepkg-template]),
[TEMPLATE_DIR=$withval], [TEMPLATE_DIR=/usr/share/makepkg-template])
# Help line for debug package suffix
AC_ARG_WITH(debug-suffix,
AS_HELP_STRING([--with-debug-suffix=name], [set the suffix for split debugging symbol packages used by makepkg]),
@ -166,6 +171,16 @@ AC_PROG_INSTALL
AC_CHECK_PROGS([PYTHON], [python2.7 python2.6 python2.5 python2 python], [false])
AC_PATH_PROGS([BASH_SHELL], [bash bash4], [false])
# check for perl 5.10.1 (needed by makepkg-template)
AC_PATH_PROG([PERL],[perl])
AC_DEFUN([AX_PROG_PERL_VERSION],
[AC_CACHE_CHECK([for Perl version $1 or later], [ax_cv_prog_perl_version],
[AS_IF(["$PERL" -e 'require v$1;' >/dev/null 2>&1],
[ax_cv_prog_perl_version=yes],
[ax_cv_prog_perl_version=no])])
AS_IF([test x"$ax_cv_prog_perl_version" = xyes], [$2], [$3])])
AX_PROG_PERL_VERSION([5.10.1], [], [AC_MSG_ERROR([perl is too old])])
AS_IF([test "x$BASH_SHELL" = "xfalse"],
AC_MSG_WARN([*** bash >= 4.1.0 is required for pacman scripts]),
[bash_version_major=`$BASH_SHELL -c 'echo "${BASH_VERSINFO[[0]]}"'`
@ -457,6 +472,9 @@ AC_DEFINE_UNQUOTED([SRCEXT], "$SRCEXT", [The file extension used by pacman sourc
# Set makepkg build script name
AC_SUBST(BUILDSCRIPT)
AC_DEFINE_UNQUOTED([BUILDSCRIPT], "$BUILDSCRIPT", [The build script name used by makepkg])
# Set makepkg-template template directory
AC_SUBST(TEMPLATE_DIR)
AC_DEFINE_UNQUOTED([TEMPLATE_DIR], "$TEMPLATE_DIR", [The template directory used by makepkg-teplate])
# Set makepkg split debugging symbol package suffix
AC_SUBST(DEBUGSUFFIX)
AC_DEFINE_UNQUOTED([DEBUGSUFFIX], "$DEBUGSUFFIX", [The suffix for debugging symbol packages used by makepkg])
@ -524,6 +542,7 @@ ${PACKAGE_NAME}:
package extension : ${PKGEXT}
source pkg extension : ${SRCEXT}
build script name : ${BUILDSCRIPT}
template directory : ${TEMPLATE_DIR}
Compilation options:
Use libcurl : ${have_libcurl}

1
doc/.gitignore vendored
View File

@ -1,6 +1,7 @@
PKGBUILD.5
libalpm.3
makepkg.8
makepkg-template.1
makepkg.conf.5
pacman.8
pacman-key.8

View File

@ -6,6 +6,7 @@
ASCIIDOC_MANS = \
pacman.8 \
makepkg.8 \
makepkg-template.1 \
repo-add.8 \
vercmp.8 \
pkgdelta.8 \
@ -21,6 +22,7 @@ DOXYGEN_MANS = $(wildcard man3/*.3)
HTML_MANPAGES = \
pacman.8.html \
makepkg.8.html \
makepkg-template.1.html \
repo-add.8.html \
vercmp.8.html \
pkgdelta.8.html \
@ -46,6 +48,7 @@ EXTRA_DIST = \
asciidoc-override.css \
pacman.8.txt \
makepkg.8.txt \
makepkg-template.1.txt \
repo-add.8.txt \
vercmp.8.txt \
pkgdelta.8.txt \
@ -147,6 +150,7 @@ $(HTML_OTHER): asciidoc.conf Makefile.am
# Dependency rules
pacman.8 pacman.8.html: pacman.8.txt
makepkg.8 makepkg.8.html: makepkg.8.txt
makepkg-template.1 makepkg-template.1.html: makepkg-template.1.txt
repo-add.8 repo-add.8.html: repo-add.8.txt
vercmp.8 vercmp.8.html: vercmp.8.txt
pkgdelta.8 pkgdelta.8.html: pkgdelta.8.txt

120
doc/makepkg-template.1.txt Normal file
View File

@ -0,0 +1,120 @@
/////
vim:set ts=4 sw=4 syntax=asciidoc noet spell spelllang=en_us:
/////
makepkg-template(1)
===================
Name
----
makepkg-template - package build templating utility
Synopsis
--------
'makepkg-template' [options]
Description
-----------
'makepkg-template' is a script to ease the work of maintaining multiple similar
PKGBUILDs. It allows you to move most of the code from the PKGBUILD into a
template file and uses markers to allow in-place updating of existing PKGBUILDs
if the template has been changed.
Template files can contain any code allowed in a PKGBUILD. You can think of
them like external files included with "." or "source", but they will be
inlined into the PKGBUILD by 'makepkg-template' so you do not depend on the
template file when building the package.
Markers are bash comments in the form of:
# template start; key=value; key2=value2; ...
and
# template end;
Currently used keys are: name (mandatory) and version. Template names are limited to
alphanumerics, "@", "+", ".", "-" and "_". Versions are limited to numbers and ".".
For initial creation there is a one line short cut which does not need an end marker:
# template input; key=value;
Using this short-cut will result in 'makepkg-template' replacing it with start
and end markers and the template code on the first run.
Template files should be stored in one directory and filenames should be
"$template_name-$version.template" with a symlink "$template_name.template"
pointing to the most recent template. If the version is not set in the marker,
'makepkg-template' will automatically use the most recent version of the
template, otherwise the specified version will be used. This allows for easier
verification of untrusted PKGBUILDs if the template is trusted. You verify the
non-template code and then use a command similar to this:
diff -u <(makepkg-template -o -) PKGBUILD
Template files may also contain markers leading to nested templates in the
resulting PKGBUILD. If you use markers in a template, please set the version
you used/tested with in the start/input marker so other people can properly
recreate from templates.
Options
-------
*-p, \--input* <build script>::
Read the package script `build script` instead of the default.
*-o, \--output* <build script>::
Write the updated file to `build script` instead of overwriting the input file.
*-n, \--newest*::
Always use the newest available template file.
*\--template-dir* <dir>::
Change the dir where we are looking for template files.
Example PKGBUILD
----------------
pkgname=perl-config-simple
pkgver=4.58
pkgrel=1
pkgdesc="simple configuration file class"
arch=('any')
license=('PerlArtistic' 'GPL')
depends=('perl')
source=("http://search.cpan.org/CPAN/authors/id/S/SH/SHERZODR/Config-Simple-${pkgver}.tar.gz")
md5sums=('f014aec54f0a1e2e880d317180fce502')
_distname="Config-Simple"
# template start; name=perl-module; version=1.0;
_distdir="${_distname}-${pkgver}"
url="https://metacpan.org/release/${_distname}"
options+=('!emptydirs')
build() {
cd "$srcdir/$_distdir"
perl Makefile.PL INSTALLDIRS=vendor
make
}
check() {
cd "$srcdir/$_distdir"
make test
}
package() {
cd "$srcdir/$_distdir"
make DESTDIR="$pkgdir" install
}
# template end;
See Also
--------
linkman:makepkg[8], linkman:PKGBUILD[5]
include::footer.txt[]

1
scripts/.gitignore vendored
View File

@ -1,4 +1,5 @@
makepkg
makepkg-template
pacman-db-upgrade
pacman-key
pacman-optimize

View File

@ -5,6 +5,7 @@ SUBDIRS = po
bin_SCRIPTS = \
$(OURSCRIPTS) \
makepkg-template \
repo-remove \
repo-elephant
@ -18,6 +19,7 @@ OURSCRIPTS = \
EXTRA_DIST = \
makepkg.sh.in \
makepkg-template.pl.in \
pacman-db-upgrade.sh.in \
pacman-key.sh.in \
pacman-optimize.sh.in \
@ -54,6 +56,7 @@ edit = sed \
-e 's|@PACKAGE_BUGREPORT[@]|$(PACKAGE_BUGREPORT)|g' \
-e 's|@PACKAGE_NAME[@]|$(PACKAGE_NAME)|g' \
-e 's|@BUILDSCRIPT[@]|$(BUILDSCRIPT)|g' \
-e 's|@TEMPLATE_DIR[@]|$(TEMPLATE_DIR)|g' \
-e 's|@DEBUGSUFFIX[@]|$(DEBUGSUFFIX)|g' \
-e "s|@INODECMD[@]|$(INODECMD)|g" \
-e 's|@SIZECMD[@]|$(SIZECMD)|g' \
@ -76,6 +79,14 @@ makepkg: \
$(srcdir)/makepkg.sh.in \
$(srcdir)/library/parseopts.sh
makepkg-template: \
$(srcdir)/makepkg-template.pl.in \
Makefile
$(AM_V_at)$(RM) -f makepkg-template
$(AM_V_GEN)$(edit) $< > $@
$(AM_V_at)chmod +x,a-w $@
pacman-db-upgrade: \
$(srcdir)/pacman-db-upgrade.sh.in \
$(srcdir)/library/output_format.sh

168
scripts/makepkg-template.pl.in Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/perl
use warnings;
use strict;
use v5.10.1;
use Cwd qw(abs_path);
use File::Spec;
use Getopt::Long;
use Pod::Usage;
my %opts = (
input => '@BUILDSCRIPT@',
template_dir => '@TEMPLATE_DIR@',
);
my $template_name_charset = qr/[[:alnum:]+_.@-]/;
my $template_marker = qr/# template/;
sub burp {
my ($file_name, @lines) = @_;
open (my $fh, ">", $file_name) || die "can't create $file_name $!" ;
print $fh @lines;
close $fh;
}
# read a template marker line and parse values into a hash
# format is "# template (start|input); key=value; key2=value2; ..."
sub parse_template_line {
my ($line, $filename, $linenumber) = @_;
my %values;
my ($marker, @elements) = split(/;\s?/, $line);
($values{command}) = ($marker =~ /$template_marker (.*)/);
foreach my $element (@elements) {
my ($key, $val) = ($element =~ /^([a-z0-9]+)=(.*)$/);
die "invalid key/value pair $filename:$linenumber: $line"
unless $key and $val;
$values{$key} = $val;
}
# end doesn't take arguments
if ($values{command} ne "end") {
if (!$values{name}) {
die "invalid template line: can't find template name\n",
"$filename:$linenumber: $line";
}
unless ($values{name} =~ /^$template_name_charset+$/) {
die "invalid chars used in name '$values{name}'. allowed: [:alnum:]+_.@-\n",
"$filename:$linenumber: $line";
}
}
return \%values;
}
# load a template, process possibly existing markers (nested templates)
sub load_template {
my ($values) = @_;
my $ret = "";
my $path;
if (!$opts{newest} and $values->{version}) {
$path = "$opts{template_dir}/$values->{name}-$values->{version}.template";
} else {
$path = "$opts{template_dir}/$values->{name}.template";
}
# resolve symlink(s) and use the real file's name for version detection
my ($version) = (abs_path($path) =~ /-([0-9.]+)[.]template$/);
if (!$version) {
die "Couldn't detect version for template '$values->{name}'";
}
my $parsed = process_file($path);
$ret .= "# template start; name=$values->{name}; version=$version;\n";
$ret .= $parsed;
$ret .= "# template end;\n";
return $ret;
}
# process input file and load templates for all markers found
sub process_file {
my ($filename) = @_;
my $ret = "";
my $nesting_level = 0;
my $linenumber = 0;
open (my $fh, "<", $filename) or die "failed to open '$filename': $!";
my @lines = <$fh>;
close $fh;
foreach my $line (@lines) {
$linenumber++;
if ($line =~ $template_marker) {
my $values = parse_template_line($line, $filename, $linenumber);
given ($values->{command}) {
when (['start', 'input']) {
if ($nesting_level == 0) {
$ret .= load_template($values);
}
}
when ('end') {
# nothing to do here, just for completeness
}
default {
die "Unknown template marker '$values->{command}'\n",
"$filename:$linenumber: $line";
}
}
$nesting_level++ if $values->{command} eq "start";
$nesting_level-- if $values->{command} eq "end";
# marker lines should never be added
next;
}
# we replace code inside blocks with the template
# so we ignore the content of the block
next if $nesting_level > 0;
$ret .= $line;
}
return $ret;
}
Getopt::Long::Configure ("bundling");
GetOptions(
"help" => sub {pod2usage(-exitval => 0, -verbose => 1); },
"h" => sub {pod2usage(-exitval => 0, -verbose => 0); },
"input|p=s" => \$opts{input},
"output|o=s" => \$opts{output},
"newest|n" => \$opts{newest},
"template-dir=s" => \$opts{template_dir},
) or pod2usage(1);
$opts{output} = $opts{input} unless $opts{output};
$opts{input} = "/dev/stdin" if $opts{input} eq "-";
$opts{output} = "/dev/stdout" if $opts{output} eq "-";
burp($opts{output}, process_file($opts{input}));
__END__
=head1 SYNOPSIS
makepkg-template [options]
Options:
--input, -p <file> Build script to read (default: @BUILDSCRIPT@)
--output, -o <file> file to output to (default: input file)
--newest, -n update templates to newest version
(default: use specified version in the template markers)
--template-dir <dir> directory to search for templates
(default: @TEMPLATE_DIR@)
=cut
# vim: set noet: