pacman/scripts/makepkg-template.pl.in

169 lines
4.1 KiB
Perl
Executable File

#!/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: