#!/usr/bin/perl
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# Copyright 2015 Marcel Denia
|
|
|
|
=head1 NAME
|
|
|
|
perlconfig.pl
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
B<perlconfig.pl> [B<-Dsymbol>=I<value>, ...] [B<-dsymbol>=I<value>, ...] I<[files]>
|
|
|
|
Generate a configuration file suitable for (cross-)compiling perl 5.
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over
|
|
|
|
=item B<-Dsymbol=value>
|
|
|
|
The symbol identified by I<name> will have the literal value I<value>.
|
|
When generating the configuration file, it's value will be printed enclosed by
|
|
single quotation marks(').
|
|
|
|
=item B<-dsymbol=value>
|
|
|
|
The symbol identified by I<name> will have the literal value I<value>.
|
|
|
|
=back
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
B<perlconfig.pl> is a program to compile and generate configuration files ready
|
|
to be used as a "config.sh" file for compiling perl 5. It does so by processing
|
|
specially-made configuration files(usually called *.config), typically augmented
|
|
by command-line definitions.
|
|
|
|
B<perlconfig.pl>'s intent is to be used in place of perl 5's own Configure
|
|
script in situations where it can not be run, like cross-compiling.
|
|
|
|
=head2 File syntax
|
|
|
|
B<perlconfig.pl>'s configuration files a consist of symbol definitions in
|
|
different variations, each one assigning a specific symbol identified by a name
|
|
some value, as well as conditional blocks in order to allow for some
|
|
flexibility.
|
|
|
|
=head3 Symbol names
|
|
|
|
A symbol name has to consist entirely of alphanumeric characters as well as
|
|
the underscore(_) character. In addition, symbol names may be prefixed with an
|
|
all-lowercase string, separated by a colon(:):
|
|
|
|
my:name=value
|
|
|
|
Having a zero-length prefix string is also valid:
|
|
|
|
:name=value
|
|
|
|
Symbols prefixed that way are called private symbols. They act exactly like
|
|
regular symbols, with the only difference being that they will B<not> be written
|
|
to the final configuration file.
|
|
|
|
=head3 Symbol definitions
|
|
|
|
A symbol definition is in the form of a simple name/value pair, separated by
|
|
an equals sign(=):
|
|
|
|
name=value
|
|
|
|
I<value> can be anything really. However, there are 3 notations, with
|
|
differences in quoting and interpolation:
|
|
|
|
=over
|
|
|
|
=item name=foo
|
|
|
|
The symbol identified by I<name> will have the literal value I<foo>.
|
|
|
|
=item name='foo'
|
|
|
|
The symbol identified by I<name> will have the literal value I<foo>.
|
|
When generating the configuration file, it's value will be printed enclosed by
|
|
single quotation marks(').
|
|
|
|
=item name="foo"
|
|
|
|
The symbol identified by I<name> will have the value of I<foo>
|
|
S<B<after interpolation>>(as described in L</Interpolation>).
|
|
When generating the configuration file, it's value will be printed enclosed by
|
|
single quotation marks(').
|
|
|
|
=back
|
|
|
|
=head3 Conditional blocks
|
|
|
|
A conditional block is of the form
|
|
|
|
(condition) {
|
|
...
|
|
}
|
|
|
|
B<perlconfig.pl> will execute everything enclosed in the curly braces({ and }),
|
|
or inside the BLOCK in Perl 5 terms, if I<condition> evaluates to any true
|
|
value. I<condition> will go through interpolation as described in
|
|
L</Interpolation>. It may contain any valid Perl 5 expression. Some common
|
|
examples are:
|
|
|
|
=over
|
|
|
|
=item $name eq 'foo'
|
|
|
|
Evaluates to true if configuration symbol I<name> is literally equal to I<foo>.
|
|
|
|
=item $name ne 'foo'
|
|
|
|
Evaluates to true if configuration symbol I<name> is B<not> literally equal to
|
|
I<foo>.
|
|
|
|
=item defined($name)
|
|
|
|
Evaluates to true if configuration symbol I<name> is defined(has any usable
|
|
value, see L<perlfunc/defined>).
|
|
|
|
=back
|
|
|
|
Conditional blocks may be nested inside conditional blocks. Note that the
|
|
opening curl brace({) has to be on the same line as your condition.
|
|
|
|
=head3 Comments
|
|
|
|
All lines starting with nothing or any number of whitespaces, followed by a
|
|
hash sign(#), are considered comments and will be completely ignored by
|
|
B<perlconfig.pl>.
|
|
|
|
=head3 Interpolation
|
|
|
|
In certain situations(see above), B<perlconfig.pl> will interpolate strings or
|
|
constructs in order to allow you to refer to configuration symbols or embed
|
|
code.
|
|
|
|
Interpolated strings are subject to the following rules:
|
|
|
|
=over
|
|
|
|
=item You may not include any single(') or double(") quotation marks.
|
|
|
|
You can use \qq in order to include double quotation marks(") in your string.
|
|
|
|
=item $name and ${name} reference configuration symbols
|
|
|
|
You can easily refer to existing configuration symbols using the commmon $name
|
|
or ${name} syntax. In case you want to refer to the perl variable named $name,
|
|
write \$name. This is useful for embedding code.
|
|
|
|
=item Perl 5 interpolation rules apply
|
|
|
|
Aside from the above, you may include anything that is also valid for an
|
|
interpolated(qq//) string in Perl 5. For instance, it's perfectly valid to
|
|
execute code using the @{[]} construct.
|
|
|
|
=back
|
|
|
|
=head1 EXAMPLES
|
|
|
|
As a simple example, consider the following configuration file, named
|
|
"example.config":
|
|
|
|
recommendation='The Perl you want is'
|
|
($:maturity eq 'stable') {
|
|
recommendation="$recommendation Perl 5"
|
|
}
|
|
($:maturity eq 'experimental') {
|
|
recommendation="$recommendation Perl 6(try Rakudo!)"
|
|
}
|
|
|
|
Executing it using these command-lines will yield the following results:
|
|
|
|
=over
|
|
|
|
=item $ perlconfig.pl -D:maturity=stable example.config
|
|
|
|
recommendation='The Perl you want is Perl 5'
|
|
|
|
=item $ perlconfig.pl -D:maturity=experimental example.config
|
|
|
|
recommendation='The Perl you want is Perl 6(try Rakudo!)'
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Marcel Denia <naoir@gmx.net>
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
Copyright 2015 Marcel Denia
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
use List::Util qw/all/;
|
|
my $symbol_name_prefix_regex = '(?:[a-z]*:)';
|
|
my $symbol_name_regex = "($symbol_name_prefix_regex?(?:[a-zA-Z0-9_]+))";
|
|
|
|
my %config;
|
|
|
|
sub interpolate {
|
|
my $string = shift;
|
|
my %options = @_;
|
|
|
|
# First, convert $foo into ${foo}
|
|
$string =~ s/(?<!\\)\$$symbol_name_regex/\${$1}/gs;
|
|
|
|
# Make ${foo} into $config{'foo'}->{value}
|
|
$string =~ s/\$\{$symbol_name_regex\}/\$config{\'$1\'}->{value}/g;
|
|
|
|
# Un-escape \$foo
|
|
$string =~ s/\\\$/\$/g;
|
|
|
|
# Turn \qq into "
|
|
$string =~ s/\\qq/\\"/g;
|
|
|
|
return $string;
|
|
}
|
|
|
|
# Parse command-line symbol definitions
|
|
while ($ARGV[0]) {
|
|
if ($ARGV[0] =~ /^-([D|d])$symbol_name_regex=(.*)$/) {
|
|
$config{$2} = { value => $3, quoted => $1 eq 'D' };
|
|
shift(@ARGV);
|
|
}
|
|
else {
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Process configuration files
|
|
my @condition_stack = ( 1 );
|
|
for my $file (@ARGV) {
|
|
open(my $fh, '<', $file) or die "Can't open $file: $!\n";
|
|
while (my $line = <$fh>) {
|
|
chomp($line);
|
|
|
|
if ($line =~ /^\s*$symbol_name_regex=(.*)$/) { # A symbol definition
|
|
if (all {$_ == 1} @condition_stack) {
|
|
my $symbol = $1;
|
|
(my $quote_begin, my $value, my $quote_end) = $2 =~ /^(['|"])?([^'"]*)(['|"])?$/;
|
|
|
|
$quote_begin = '' unless defined $quote_begin;
|
|
$quote_end = '' unless defined $quote_end;
|
|
die "$file:$.: Unmatched quotes in \"$line\"\n" unless $quote_begin eq $quote_end;
|
|
|
|
if ($quote_begin eq '"') {
|
|
$config{$symbol} = { value => eval('"' . interpolate($2) . '"'), quoted => 1 };
|
|
}
|
|
else {
|
|
$config{$symbol} = { value => $2, quoted => $quote_begin eq '\'' };
|
|
}
|
|
}
|
|
}
|
|
elsif ($line =~ /^\s*\((.*)\)\s?{$/) { # A conditional block
|
|
if (eval(interpolate($1))) {
|
|
push(@condition_stack, 1);
|
|
}
|
|
else {
|
|
push(@condition_stack, 0);
|
|
}
|
|
}
|
|
elsif ($line =~ /^\s*}$/) { # Closing a conditional block
|
|
pop(@condition_stack);
|
|
die "$file:$.: Closing non-existent block\n" unless @condition_stack;
|
|
}
|
|
elsif ($line =~ (/^\s*$/) || ($line =~ /^\s*#/)) { # An empty line or comment
|
|
}
|
|
else {
|
|
die "$file:$.: Malformed line: \"$line\"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
# Output
|
|
for (sort(keys(%config))) {
|
|
my $quote = $config{$_}->{quoted} ? '\'' : '';
|
|
print("$_=$quote$config{$_}->{value}$quote\n") unless $_ =~ /^$symbol_name_prefix_regex/;
|
|
}
|