1
0

perlconfig.pl 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/perl
  2. # This program is free software: you can redistribute it and/or modify
  3. # it under the terms of the GNU General Public License as published by
  4. # the Free Software Foundation, either version 3 of the License, or
  5. # (at your option) any later version.
  6. #
  7. # This program is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. #
  15. # Copyright 2015 Marcel Denia
  16. =head1 NAME
  17. perlconfig.pl
  18. =head1 SYNOPSIS
  19. B<perlconfig.pl> [B<-Dsymbol>=I<value>, ...] [B<-dsymbol>=I<value>, ...] I<[files]>
  20. Generate a configuration file suitable for (cross-)compiling perl 5.
  21. =head1 OPTIONS
  22. =over
  23. =item B<-Dsymbol=value>
  24. The symbol identified by I<name> will have the literal value I<value>.
  25. When generating the configuration file, it's value will be printed enclosed by
  26. single quotation marks(').
  27. =item B<-dsymbol=value>
  28. The symbol identified by I<name> will have the literal value I<value>.
  29. =back
  30. =head1 DESCRIPTION
  31. B<perlconfig.pl> is a program to compile and generate configuration files ready
  32. to be used as a "config.sh" file for compiling perl 5. It does so by processing
  33. specially-made configuration files(usually called *.config), typically augmented
  34. by command-line definitions.
  35. B<perlconfig.pl>'s intent is to be used in place of perl 5's own Configure
  36. script in situations where it can not be run, like cross-compiling.
  37. =head2 File syntax
  38. B<perlconfig.pl>'s configuration files a consist of symbol definitions in
  39. different variations, each one assigning a specific symbol identified by a name
  40. some value, as well as conditional blocks in order to allow for some
  41. flexibility.
  42. =head3 Symbol names
  43. A symbol name has to consist entirely of alphanumeric characters as well as
  44. the underscore(_) character. In addition, symbol names may be prefixed with an
  45. all-lowercase string, separated by a colon(:):
  46. my:name=value
  47. Having a zero-length prefix string is also valid:
  48. :name=value
  49. Symbols prefixed that way are called private symbols. They act exactly like
  50. regular symbols, with the only difference being that they will B<not> be written
  51. to the final configuration file.
  52. =head3 Symbol definitions
  53. A symbol definition is in the form of a simple name/value pair, separated by
  54. an equals sign(=):
  55. name=value
  56. I<value> can be anything really. However, there are 3 notations, with
  57. differences in quoting and interpolation:
  58. =over
  59. =item name=foo
  60. The symbol identified by I<name> will have the literal value I<foo>.
  61. =item name='foo'
  62. The symbol identified by I<name> will have the literal value I<foo>.
  63. When generating the configuration file, it's value will be printed enclosed by
  64. single quotation marks(').
  65. =item name="foo"
  66. The symbol identified by I<name> will have the value of I<foo>
  67. S<B<after interpolation>>(as described in L</Interpolation>).
  68. When generating the configuration file, it's value will be printed enclosed by
  69. single quotation marks(').
  70. =back
  71. =head3 Conditional blocks
  72. A conditional block is of the form
  73. (condition) {
  74. ...
  75. }
  76. B<perlconfig.pl> will execute everything enclosed in the curly braces({ and }),
  77. or inside the BLOCK in Perl 5 terms, if I<condition> evaluates to any true
  78. value. I<condition> will go through interpolation as described in
  79. L</Interpolation>. It may contain any valid Perl 5 expression. Some common
  80. examples are:
  81. =over
  82. =item $name eq 'foo'
  83. Evaluates to true if configuration symbol I<name> is literally equal to I<foo>.
  84. =item $name ne 'foo'
  85. Evaluates to true if configuration symbol I<name> is B<not> literally equal to
  86. I<foo>.
  87. =item defined($name)
  88. Evaluates to true if configuration symbol I<name> is defined(has any usable
  89. value, see L<perlfunc/defined>).
  90. =back
  91. Conditional blocks may be nested inside conditional blocks. Note that the
  92. opening curl brace({) has to be on the same line as your condition.
  93. =head3 Comments
  94. All lines starting with nothing or any number of whitespaces, followed by a
  95. hash sign(#), are considered comments and will be completely ignored by
  96. B<perlconfig.pl>.
  97. =head3 Interpolation
  98. In certain situations(see above), B<perlconfig.pl> will interpolate strings or
  99. constructs in order to allow you to refer to configuration symbols or embed
  100. code.
  101. Interpolated strings are subject to the following rules:
  102. =over
  103. =item You may not include any single(') or double(") quotation marks.
  104. You can use \qq in order to include double quotation marks(") in your string.
  105. =item $name and ${name} reference configuration symbols
  106. You can easily refer to existing configuration symbols using the commmon $name
  107. or ${name} syntax. In case you want to refer to the perl variable named $name,
  108. write \$name. This is useful for embedding code.
  109. =item Perl 5 interpolation rules apply
  110. Aside from the above, you may include anything that is also valid for an
  111. interpolated(qq//) string in Perl 5. For instance, it's perfectly valid to
  112. execute code using the @{[]} construct.
  113. =back
  114. =head1 EXAMPLES
  115. As a simple example, consider the following configuration file, named
  116. "example.config":
  117. recommendation='The Perl you want is'
  118. ($:maturity eq 'stable') {
  119. recommendation="$recommendation Perl 5"
  120. }
  121. ($:maturity eq 'experimental') {
  122. recommendation="$recommendation Perl 6(try Rakudo!)"
  123. }
  124. Executing it using these command-lines will yield the following results:
  125. =over
  126. =item $ perlconfig.pl -D:maturity=stable example.config
  127. recommendation='The Perl you want is Perl 5'
  128. =item $ perlconfig.pl -D:maturity=experimental example.config
  129. recommendation='The Perl you want is Perl 6(try Rakudo!)'
  130. =back
  131. =head1 AUTHOR
  132. Marcel Denia <naoir@gmx.net>
  133. =head1 COPYRIGHT AND LICENSE
  134. Copyright 2015 Marcel Denia
  135. This program is free software: you can redistribute it and/or modify
  136. it under the terms of the GNU General Public License as published by
  137. the Free Software Foundation, either version 3 of the License, or
  138. (at your option) any later version.
  139. This program is distributed in the hope that it will be useful,
  140. but WITHOUT ANY WARRANTY; without even the implied warranty of
  141. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  142. GNU General Public License for more details.
  143. You should have received a copy of the GNU General Public License
  144. along with this program. If not, see <http://www.gnu.org/licenses/>.
  145. =cut
  146. use strict;
  147. use warnings;
  148. use List::Util qw/all/;
  149. my $symbol_name_prefix_regex = '(?:[a-z]*:)';
  150. my $symbol_name_regex = "($symbol_name_prefix_regex?(?:[a-zA-Z0-9_]+))";
  151. my %config;
  152. sub interpolate {
  153. my $string = shift;
  154. my %options = @_;
  155. # First, convert $foo into ${foo}
  156. $string =~ s/(?<!\\)\$$symbol_name_regex/\${$1}/gs;
  157. # Make ${foo} into $config{'foo'}->{value}
  158. $string =~ s/\$\{$symbol_name_regex\}/\$config{\'$1\'}->{value}/g;
  159. # Un-escape \$foo
  160. $string =~ s/\\\$/\$/g;
  161. # Turn \qq into "
  162. $string =~ s/\\qq/\\"/g;
  163. return $string;
  164. }
  165. # Parse command-line symbol definitions
  166. while ($ARGV[0]) {
  167. if ($ARGV[0] =~ /^-([D|d])$symbol_name_regex=(.*)$/) {
  168. $config{$2} = { value => $3, quoted => $1 eq 'D' };
  169. shift(@ARGV);
  170. }
  171. else {
  172. last;
  173. }
  174. }
  175. # Process configuration files
  176. my @condition_stack = ( 1 );
  177. for my $file (@ARGV) {
  178. open(my $fh, '<', $file) or die "Can't open $file: $!\n";
  179. while (my $line = <$fh>) {
  180. chomp($line);
  181. if ($line =~ /^\s*$symbol_name_regex=(.*)$/) { # A symbol definition
  182. if (all {$_ == 1} @condition_stack) {
  183. my $symbol = $1;
  184. (my $quote_begin, my $value, my $quote_end) = $2 =~ /^(['|"])?([^'"]*)(['|"])?$/;
  185. $quote_begin = '' unless defined $quote_begin;
  186. $quote_end = '' unless defined $quote_end;
  187. die "$file:$.: Unmatched quotes in \"$line\"\n" unless $quote_begin eq $quote_end;
  188. if ($quote_begin eq '"') {
  189. $config{$symbol} = { value => eval('"' . interpolate($2) . '"'), quoted => 1 };
  190. }
  191. else {
  192. $config{$symbol} = { value => $2, quoted => $quote_begin eq '\'' };
  193. }
  194. }
  195. }
  196. elsif ($line =~ /^\s*\((.*)\)\s?{$/) { # A conditional block
  197. if (eval(interpolate($1))) {
  198. push(@condition_stack, 1);
  199. }
  200. else {
  201. push(@condition_stack, 0);
  202. }
  203. }
  204. elsif ($line =~ /^\s*}$/) { # Closing a conditional block
  205. pop(@condition_stack);
  206. die "$file:$.: Closing non-existent block\n" unless @condition_stack;
  207. }
  208. elsif ($line =~ (/^\s*$/) || ($line =~ /^\s*#/)) { # An empty line or comment
  209. }
  210. else {
  211. die "$file:$.: Malformed line: \"$line\"\n";
  212. }
  213. }
  214. }
  215. # Output
  216. for (sort(keys(%config))) {
  217. my $quote = $config{$_}->{quoted} ? '\'' : '';
  218. print("$_=$quote$config{$_}->{value}$quote\n") unless $_ =~ /^$symbol_name_prefix_regex/;
  219. }