123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- #!/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/;
- }
|