This allows for somewhat easy templating for PKGBUILDs. Signed-off-by: Florian Pritz <bluewind@xinu.at> --- Andrew reviewed most of the perl script already, I just got rid of a dependency on File::Slurp afterwards. v2: Got rid of %% for "template input" markers, they now also use #. Comments on the man page are very welcome. doc/.gitignore | 1 + doc/Makefile.am | 4 ++ doc/makepkg-template.1.txt | 89 +++++++++++++++++++++++ scripts/.gitignore | 1 + scripts/Makefile.am | 7 ++ scripts/makepkg-template.pl.in | 159 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 261 insertions(+) create mode 100644 doc/makepkg-template.1.txt create mode 100755 scripts/makepkg-template.pl.in diff --git a/doc/.gitignore b/doc/.gitignore index a96ddb3..ad496ce 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,6 +1,7 @@ PKGBUILD.5 libalpm.3 makepkg.8 +makepkg-template.1 makepkg.conf.5 pacman.8 pacman-key.8 diff --git a/doc/Makefile.am b/doc/Makefile.am index bcb05b7..cb01255 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 diff --git a/doc/makepkg-template.1.txt b/doc/makepkg-template.1.txt new file mode 100644 index 0000000..40ac3e1 --- /dev/null +++ b/doc/makepkg-template.1.txt @@ -0,0 +1,89 @@ +///// +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. + +Markers are bash comments in the form of: + + # template (start|end); key=value; key2=value2 + +and + + # template end; + +Currently used keys are: name (mandatory), version + +For initial creation there is a shortcut: + + # template input; key=value; + +If the version is not set, 'makepkg-template' will automatically use the most +recent version of the template, otherwise the specified version will be used +allowing for easier verification of untrused PKGBUILDs if the template is trusted. + +Template files may also contain markers leading to nested templates in the +resulting PKGBUILD. + +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. + +Options +------- + +*-p, \--input* <buildscript>:: + Read the package script `buildscript` instead of the `PKGBUILD` default. + +*-o, \--output* <buildscript>:: + Write the updated file to `buildscript` 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=foo-bar + # template start; name=foo-module; version=1.2.3 + _srcdir="$pkgname-$pkgver" + + build() { + cd "$_srcdir" + ./configure + make + } + # template start; name=package-simple; version=1.0 + package() + cd "$_srcdir" + make DESTDIR="$pkgdir" install + } + # template end; + # template end; + + + +See Also +-------- +linkman:makepkg[8], linkman:PKGBUILD[5] + +include::footer.txt[] diff --git a/scripts/.gitignore b/scripts/.gitignore index 9e403bf..26e088b 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,4 +1,5 @@ makepkg +makepkg-template pacman-db-upgrade pacman-key pacman-optimize diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 784b180..bc5d588 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -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 \ @@ -76,6 +78,11 @@ makepkg: \ $(srcdir)/makepkg.sh.in \ $(srcdir)/library/parseopts.sh +makepkg-template: \ + $(srcdir)/makepkg-template.pl.in + $(AM_V_at)$(RM) -f makepkg-template + $(AM_V_at)cp $< $@ + pacman-db-upgrade: \ $(srcdir)/pacman-db-upgrade.sh.in \ $(srcdir)/library/output_format.sh diff --git a/scripts/makepkg-template.pl.in b/scripts/makepkg-template.pl.in new file mode 100755 index 0000000..6c11c24 --- /dev/null +++ b/scripts/makepkg-template.pl.in @@ -0,0 +1,159 @@ +#!/usr/bin/perl +use warnings; +use strict; +use v5.14; +use Cwd qw(abs_path); +use File::Spec; +use Getopt::Long; +use Pod::Usage; + +my %opts = ( + input => 'PKGBUILD', + template_dir => '/usr/share/makepkg-template', +); + +sub burp { + my ($file_name) = shift; + open (my $fh, ">", $file_name) || die "can't create $file_name $!" ; + print $fh @_; +} + +# read a template marker line and parse values into a hash +# format is "# template (start|input|end); key=value; key2=value2; ..." +sub parse_template_line { + my ($line, $filename, $linenumber) = @_; + my %values; + + my @elements = split(/;\s?/, $line); + + foreach my $element (@elements) { + given($element) { + when (/^# template (.*)$/) { + $values{template} = $1; + } + when (/^([a-z0-9_]+)=(.*);?$/) { + $values{$1} = $2; + } + } + } + + if (!$values{name}) { + die "invalid template line: can't find template name", + "$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 basename for version detection + my $basename = (File::Spec->splitpath(abs_path($path)))[2]; + my ($version) = ($basename =~ /^[^\.]+\.([0-9\.]+)\.template$/); + + 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 $in_block = 0; + my $linenumber = 0; + + open FH, "<", $filename or die "failed to open '$filename': $!"; + my @lines = <FH>; + close FH; + + foreach my $line (@lines) { + my $add_line = 1; + $linenumber++; + + if ($line =~ /^# template ([^;]+);?/) { + given ($1) { + # special marker to insert a template for the first time + when ('input') { + my $values = parse_template_line($line, $filename, $linenumber); + $ret .= load_template($values); + $add_line = 0; + } + + # marker followed by code from the template and an end + when ('start') { + my $values = parse_template_line($line, $filename, $linenumber); + + # only process the first template level here + # load_template calls us again if templates are nested + if ($in_block == 0) { + $ret .= load_template($values); + } + $in_block++; + } + + when ('end') { + $in_block--; + $add_line = 0; + } + + default { + die "Unknown template marker '$1'", + "$filename:$linenumber: $line"; + } + } + } + + if ($in_block > 0) { + $add_line = 0; + } + + $ret .= "$line" if $add_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}; + +my $output = process_file($opts{input}); + +burp($opts{output}, $output); + +__END__ +=head1 SYNOPSIS + +makepkg-template [options] + + Options: + --input, -p <file> PKGBUILD to read (default: ./PKGBUILD) + --output, -o <file> file to output to (default: input file) + --newest, -n update templates to newest possible version + (default: use specified version in the template markers) + --template-dir <dir> dir to search for templates (default: /usr/share/makepkg-template) + +=cut -- 1.8.2.2