[pacman-dev] [RFC] [PATCH] Add makepkg-template
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. 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 | 160 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 262 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..cea470a --- /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..d03fd33 --- /dev/null +++ b/scripts/makepkg-template.pl.in @@ -0,0 +1,160 @@ +#!/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++; + + # special marker to insert a template for the first time + # start with %% in order to create an invalid bash script until replaced + if ($line =~ /^%% template input/) { + my $values = parse_template_line($line, $filename, $linenumber); + $ret .= load_template($values); + $add_line = 0; + } + + if ($line =~ /^# template ([^;]+);?/) { + given ($1) { + # 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
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
On 02/05/13 08:12, Florian Pritz wrote:
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
Just "template start" here, or it is end too?
+and + + # template end; + +Currently used keys are: name (mandatory), version +
No checksum?
+For initial creation there is a shortcut: + + # template input; key=value;
I do not understand this from the (lack of) description.
+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.
This versioning of templates is not described anywhere....
+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.
... oh here it is. Describe the versioning before you refer to the version. Also, what exactly is inside the template file? Just the bash?
+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 +----------------
I'd go for a real life example (e.g. a perl module), rather than a plain ./configure.
+ 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
Why the RM? Anyway, this will need to change with the comments below.
+ $(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',
Needs to use whatever the build script name is configured to build time.
+ template_dir => '/usr/share/makepkg-template',
Also needs to be configurable at build time.
+); + +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; ..."
"input" ?
+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$/);
Document the restrictions on template names. Does that say no "." is allowed?
+ 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)
Configured.
+ --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)
Configured.
+ +=cut
I know too little perl to comment beyond that. Allan
This allows for somewhat easy templating for PKGBUILDs. Signed-off-by: Florian Pritz <bluewind@xinu.at> --- v3: - improved manpage - added --with-template-dir to configure - use @BUILDSCRIPT@ instead of hardcoded default - switched to same filename format as makepkg/pacman (templatename-version.template) and allowed some more chars for templatename - added some sanity checks for templatename/version configure.ac | 9 +++ doc/.gitignore | 1 + doc/Makefile.am | 4 + doc/makepkg-template.1.txt | 132 ++++++++++++++++++++++++++++++++ scripts/.gitignore | 1 + scripts/Makefile.am | 11 +++ scripts/makepkg-template.pl.in | 170 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+) create mode 100644 doc/makepkg-template.1.txt create mode 100755 scripts/makepkg-template.pl.in diff --git a/configure.ac b/configure.ac index 81bc1b3..3b0c62c 100644 --- a/configure.ac +++ b/configure.ac @@ -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(template-dir, + AS_HELP_STRING([--with-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]), @@ -457,6 +462,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 dir +AC_SUBST(TEMPLATE_DIR) +AC_DEFINE_UNQUOTED([TEMPLATE_DIR], "$TEMPLATE_DIR", [The template dir 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 +532,7 @@ ${PACKAGE_NAME}: package extension : ${PKGEXT} source pkg extension : ${SRCEXT} build script name : ${BUILDSCRIPT} + template dir : ${TEMPLATE_DIR} Compilation options: Use libcurl : ${have_libcurl} 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..b3ed25e --- /dev/null +++ b/doc/makepkg-template.1.txt @@ -0,0 +1,132 @@ +///// +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 if you wish to build 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. Values are limited to +alphanumerics, "@", "+", ".", "-" and "_". + +For initial creation there is a one line shortcut which doesn't need an end: + + # template input; key=value; + +If you use this 'makepkg-template' will replace 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 untrused PKGBUILDs if the template is +trusted. You verify the non-template code and then use a command similar to +this: + + diff -u PKGBUILD <(makepkg-template -o /dev/stdout) + +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* <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=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; + options=(!emptydirs) + _distdir="${_distname}-${pkgver}" + url="https://metacpan.org/release/${_distname}" + + build() { + export PERL_MM_USE_DEFAULT=1 PERL5LIB="" \ + PERL_AUTOINSTALL=--skipdeps \ + PERL_MM_OPT="INSTALLDIRS=vendor DESTDIR='$pkgdir'" \ + PERL_MB_OPT="--installdirs vendor --destdir '$pkgdir'" \ + MODULEBUILDRC=/dev/null + + cd "$srcdir/$_distdir" + /usr/bin/perl Makefile.PL + make + } + + check() { + export PERL_MM_USE_DEFAULT=1 PERL5LIB="" + cd "$srcdir/$_distdir" + make test + } + + package() { + cd "$srcdir/$_distdir" + make DESTDIR="$pkgdir" install + find "$pkgdir" -name .packlist -o -name perllocal.pod -delete + } + # 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..1f3bae2 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 \ @@ -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 diff --git a/scripts/makepkg-template.pl.in b/scripts/makepkg-template.pl.in new file mode 100755 index 0000000..c7e753e --- /dev/null +++ b/scripts/makepkg-template.pl.in @@ -0,0 +1,170 @@ +#!/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 => '@BUILDSCRIPT@', + template_dir => '@TEMPLATE_DIR@', +); + +my $template_name_charset = qr/[[:alnum:]+_\.@-]/; + +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); 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\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 basename for version detection + my $basename = (File::Spec->splitpath(abs_path($path)))[2]; + my ($version) = ($basename =~ /^$template_name_charset+-([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 $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: @BUILDSCRIPT@) + --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: @TEMPLATE_DIR@) + +=cut -- 1.8.2.2
A few more comments now that I've had time to do a more in-depth review. On 05/02/13 at 09:36am, Florian Pritz wrote:
This allows for somewhat easy templating for PKGBUILDs.
Signed-off-by: Florian Pritz <bluewind@xinu.at> ---
v3: - improved manpage - added --with-template-dir to configure - use @BUILDSCRIPT@ instead of hardcoded default - switched to same filename format as makepkg/pacman (templatename-version.template) and allowed some more chars for templatename - added some sanity checks for templatename/version
configure.ac | 9 +++ doc/.gitignore | 1 + doc/Makefile.am | 4 + doc/makepkg-template.1.txt | 132 ++++++++++++++++++++++++++++++++ scripts/.gitignore | 1 + scripts/Makefile.am | 11 +++ scripts/makepkg-template.pl.in | 170 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+) create mode 100644 doc/makepkg-template.1.txt create mode 100755 scripts/makepkg-template.pl.in
<snip>
diff --git a/scripts/makepkg-template.pl.in b/scripts/makepkg-template.pl.in new file mode 100755 index 0000000..c7e753e --- /dev/null +++ b/scripts/makepkg-template.pl.in @@ -0,0 +1,170 @@ +#!/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 => '@BUILDSCRIPT@', + template_dir => '@TEMPLATE_DIR@', +); + +my $template_name_charset = qr/[[:alnum:]+_\.@-]/;
No need to escape the period, they lose their magic inside []. Let's also add a $template_marker = qr/# template/ here.
+ +sub burp { + my ($file_name) = shift;
Let's be consistent in unpacking @_. my ($file_name, @lines) = @_; ... print $fh @lines;
+ open (my $fh, ">", $file_name) || die "can't create $file_name $!" ; + print $fh @_;
Let's explicitly 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 @elements = split(/;\s?/, $line); + + foreach my $element (@elements) { + given($element) { + when (/^# template (.*)$/) {
The first element is always going to be special. Let's just grab it separately and handle it upfront. my ($marker, @elements) = split... ($values{command}) = ($marker =~ /$template_marker (.*)/); Then this loop can be reduced to something like this (note the split above will take care of any semi-colons): my ($key, $val) = ($element =~ /^([a-z0-9]+)=(.*)$/) die "invalid key/value pair $filename:linenumber: $line" unless $key and $val; $values{$key} = $val;
+ $values{template} = $1; + } + when (/^([a-z0-9_]+)=(.*);?$/) { + $values{$1} = $2; + } + } + } + + 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 basename for version detection + my $basename = (File::Spec->splitpath(abs_path($path)))[2]; + my ($version) = ($basename =~ /^$template_name_charset+-([0-9\.]+)\.template$/);
We're already anchored at the end of the path and I don't see any reason to check for a valid template name at this stage, so no need for the splitpath call here or including $template_name_charset in the regex. 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 $in_block = 0; + my $linenumber = 0; + + open FH, "<", $filename or die "failed to open '$filename': $!"; + my @lines = <FH>; + close FH;
We may as well be consistent about using simple scalars with open. open (my $fh...
+ + foreach my $line (@lines) { + my $add_line = 1; + $linenumber++; + + if ($line =~ /^# template ([^;]+);?/) {
I don't think we should partially parse the line here only to parse it fully in parse_template_line. That will just require us to keep this regex in sync with the real parser. if($line =~ $template_marker) { my $values = parse_template_line(...); # either move the checks for template name here or skip them in # parse_template_line for 'template end' given ($value->{command}) { ...
+ 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;
This $in_block/$add_line mechanism for tracking whether or not to append the line is a little odd. I think it would be better to either: convert this loop to a c-style loop and immediately skip to the end of the template block as soon as we see 'template start' or use next to skip to the next line instead of setting $add_line = 0. Also, as it stands now, it will skip over a nested 'template start' inside a block, but it will expand 'template input' inside the block.
+ } + 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});
This temporary variable doesn't hurt anything, but it doesn't serve any purpose either
+ +burp($opts{output}, $output); + +__END__ +=head1 SYNOPSIS + +makepkg-template [options] + + Options: + --input, -p <file> PKGBUILD to read (default: @BUILDSCRIPT@) + --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: @TEMPLATE_DIR@) + +=cut
vim modeline: # vim: set noet:
-- 1.8.2.2
On 02.05.2013 06:19, Allan McRae wrote:
+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
Just "template start" here, or it is end too?
Typo
+and + + # template end; + +Currently used keys are: name (mandatory), version +
No checksum?
I'd have to recalculate the checksum for the content (can't trust the content, remember?) and then build the chain of templates (since checksums depend on nested templates) and then compare that. Way easier to just rebuild the whole thing and run diff. Something along the lines of "diff PKGBUILD <(makepkg-template -o /dev/stdout)".
+For initial creation there is a shortcut: + + # template input; key=value;
I do not understand this from the (lack of) description.
See next patch for a (hopefully) better version.
+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.
This versioning of templates is not described anywhere....
+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.
... oh here it is. Describe the versioning before you refer to the version. Also, what exactly is inside the template file? Just the bash?
+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 +----------------
I'd go for a real life example (e.g. a perl module), rather than a plain ./configure.
+ 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
Why the RM? Anyway, this will need to change with the comments below.
+ $(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',
Needs to use whatever the build script name is configured to build time.
+ template_dir => '/usr/share/makepkg-template',
Also needs to be configurable at build time.
Alright, maybe my fear of autotools was a little bit irrational. I've copied some code for @BUILDSCRIPT@ and it seems to work, please check the next patch.
+); + +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; ..."
"input" ?
I think you ment "end", start and input both take arguments/options, end doesn't. Fixed.
+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$/);
Document the restrictions on template names. Does that say no "." is allowed?
Allowed [[:alnum:]+_\.@-] and documented. The separator between name and version is now also - to match makepkg/pacman.
+ 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)
Configured.
+ --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)
Configured.
+ +=cut
I know too little perl to comment beyond that.
Thanks :)
participants (3)
-
Allan McRae
-
Andrew Gregory
-
Florian Pritz