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]), AS_HELP_STRING([--with-buildscript=name], [set the build script name used by makepkg]),
[BUILDSCRIPT=$withval], [BUILDSCRIPT=PKGBUILD]) [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 # Help line for debug package suffix
AC_ARG_WITH(debug-suffix, AC_ARG_WITH(debug-suffix,
AS_HELP_STRING([--with-debug-suffix=name], [set the suffix for split debugging symbol packages used by makepkg]), 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_CHECK_PROGS([PYTHON], [python2.7 python2.6 python2.5 python2 python], [false])
AC_PATH_PROGS([BASH_SHELL], [bash bash4], [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"], AS_IF([test "x$BASH_SHELL" = "xfalse"],
AC_MSG_WARN([*** bash >= 4.1.0 is required for pacman scripts]), AC_MSG_WARN([*** bash >= 4.1.0 is required for pacman scripts]),
[bash_version_major=`$BASH_SHELL -c 'echo "${BASH_VERSINFO[[0]]}"'` [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 # Set makepkg build script name
AC_SUBST(BUILDSCRIPT) AC_SUBST(BUILDSCRIPT)
AC_DEFINE_UNQUOTED([BUILDSCRIPT], "$BUILDSCRIPT", [The build script name used by makepkg]) 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 # Set makepkg split debugging symbol package suffix
AC_SUBST(DEBUGSUFFIX) AC_SUBST(DEBUGSUFFIX)
AC_DEFINE_UNQUOTED([DEBUGSUFFIX], "$DEBUGSUFFIX", [The suffix for debugging symbol packages used by makepkg]) AC_DEFINE_UNQUOTED([DEBUGSUFFIX], "$DEBUGSUFFIX", [The suffix for debugging symbol packages used by makepkg])
@ -524,6 +542,7 @@ ${PACKAGE_NAME}:
package extension : ${PKGEXT} package extension : ${PKGEXT}
source pkg extension : ${SRCEXT} source pkg extension : ${SRCEXT}
build script name : ${BUILDSCRIPT} build script name : ${BUILDSCRIPT}
template directory : ${TEMPLATE_DIR}
Compilation options: Compilation options:
Use libcurl : ${have_libcurl} Use libcurl : ${have_libcurl}

1
doc/.gitignore vendored
View File

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

View File

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

View File

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