Bug 569365 - Remove preprocessor.pl and replace it with Preprocessor.py. r=ted

--HG--
rename : config/Expression.py => js/src/config/Expression.py
rename : config/Preprocessor.py => js/src/config/Preprocessor.py
This commit is contained in:
Mike Hommey 2011-05-16 15:51:21 +02:00
parent a733c6b3cf
commit 2d708facec
11 changed files with 693 additions and 1601 deletions

View File

@ -116,12 +116,12 @@ $(DIST)/branding:
libs::
@if test -f "$(LOCALE_SRCDIR)/existing-profile-defaults.js"; then \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(LOCALE_SRCDIR)/existing-profile-defaults.js > $(FINAL_TARGET)/defaults/existing-profile-defaults.js; \
fi
install::
@if test -f "$(LOCALE_SRCDIR)/existing-profile-defaults.js"; then \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(LOCALE_SRCDIR)/existing-profile-defaults.js > $(DESTDIR)$(mozappdir)/defaults/existing-profile-defaults.js; \
fi

View File

@ -121,7 +121,7 @@ endif
ifdef WRAP_SYSTEM_INCLUDES
export::
if test ! -d system_wrappers; then mkdir system_wrappers; fi
$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
-DBUILD_STATIC_LIBS=$(BUILD_STATIC_LIBS) \
-DMOZ_TREE_CAIRO=$(MOZ_TREE_CAIRO) \
-DMOZ_TREE_PIXMAN=$(MOZ_TREE_PIXMAN) \

View File

@ -1,671 +0,0 @@
#!/usr/bin/perl -w
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Preprocessor
# Version 1.1
#
# Copyright (c) 2002, 2003, 2004 by Ian Hickson
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Thanks to bryner and bsmedberg for suggestions.
# Thanks to jon rekai for a patch to not require File::Spec 0.8.
use strict;
# takes as arguments the files to process
# defaults to stdin
# output to stdout
my $stack = new stack;
my $marker = '#';
# command line arguments
my @includes;
while ($_ = $ARGV[0], defined($_) && /^-./) {
shift;
last if /^--$/os;
if (/^-D(.*)$/os) {
for ($1) {
if (/^([\w\.]+)=(.*)$/os) {
$stack->define($1, $2);
} elsif (/^([\w\.]+)$/os) {
$stack->define($1, 1);
} else {
die "$0: invalid argument to -D: $_\n";
}
}
} elsif (/^-F(.*)$/os) {
for ($1) {
if (/^(\w+)$/os) {
$stack->filter($1, 1);
} else {
die "$0: invalid argument to -F: $_\n";
}
}
} elsif (/^-I(.*)$/os) {
push(@includes, $1);
} elsif (/^-E$/os) {
foreach (keys %ENV) {
# define all variables that have valid names
$stack->define($_, $ENV{$_}) unless m/\W/;
}
} elsif (/^-d$/os) {
$stack->{'dependencies'} = 1;
} elsif (/^--line-endings=crlf$/os) {
$stack->{'lineEndings'} = "\x0D\x0A";
} elsif (/^--line-endings=cr$/os) {
$stack->{'lineEndings'} = "\x0D";
} elsif (/^--line-endings=lf$/os) {
$stack->{'lineEndings'} = "\x0A";
} elsif (/^--line-endings=(.+)$/os) {
die "$0: unrecognised line ending: $1\n";
} elsif (/^--marker=(.)$/os) {
$marker = $1;
} else {
die "$0: invalid argument: $_\n";
}
}
unshift(@ARGV, '-') unless @ARGV;
unshift(@ARGV, @includes);
# do the work
foreach (@ARGV) { include($stack, $_); }
exit(0);
########################################################################
package main;
use File::Spec;
use File::Spec::Unix; # on all platforms, because the #include syntax is unix-based
# Note: Ideally we would use File::Spec 0.8. When this becomes
# possible, add "0.8" to the first "use" line above, then replace
# occurrences of "::_0_8::" with "->" below. And remove the code for
# File::Spec 0.8 much lower down the file.
sub include {
my($stack, $filename) = @_;
my $directory = $stack->{'variables'}->{'DIRECTORY'};
if ($filename ne '-') {
$filename = File::Spec::_0_8::rel2abs($filename, $directory);
# splitpath expects forward-slash paths on windows, so we have to
# change the slashes if using Activestate Perl.
$filename =~ s?\\?/?g if "$^O" eq "MSWin32";
my($volume, $path) = File::Spec::_0_8::splitpath($filename);
$directory = File::Spec::_0_8::catpath($volume, $path, '');
}
local $stack->{'variables'}->{'DIRECTORY'} = $directory;
local $stack->{'variables'}->{'FILE'} = $filename;
local $stack->{'variables'}->{'LINE'} = 0;
local *FILE;
open(FILE, $filename) or die "Couldn't open $filename: $!\n";
my $lineout = 0;
while (<FILE>) {
# on cygwin, line endings are screwed up, so normalise them.
s/[\x0D\x0A]+$/\n/os if ($^O eq 'msys' || $^O eq 'cygwin' || "$^O" eq "MSWin32");
$stack->newline;
if (/^\Q$marker\E([a-z]+)\n?$/os) { # argumentless processing instruction
process($stack, $1);
} elsif (/^\Q$marker\E([a-z]+)\s(.*?)\n?$/os) { # processing instruction with arguments
process($stack, $1, $2);
} elsif (/^\Q$marker\E/os) { # comment
# ignore it
} elsif ($stack->enabled) {
next if $stack->{'dependencies'};
# set the current line number in JavaScript if necessary
my $linein = $stack->{'variables'}->{'LINE'};
if (++$lineout != $linein) {
if ($filename =~ /\.js(|\.in)$/o) {
$stack->print("//\@line $linein \"$filename\"\n")
}
$lineout = $linein;
}
# print it, including any newlines
$stack->print(filtered($stack, $_));
}
}
close(FILE);
}
sub process {
my($stack, $instruction, @arguments) = @_;
my $method = 'preprocessor'->can($instruction);
if (not defined($method)) {
fatal($stack, 'unknown instruction', $instruction);
}
eval { &$method($stack, @arguments) };
if ($@) {
fatal($stack, "error evaluating $instruction:", $@);
}
}
sub filtered {
my($stack, $text) = @_;
foreach my $filter (sort keys %{$stack->{'filters'}}) {
next unless $stack->{'filters'}->{$filter};
my $method = 'filter'->can($filter);
if (not defined($method)) {
fatal($stack, 'unknown filter', $filter);
}
$text = eval { &$method($stack, $text) };
if ($@) {
fatal($stack, "error using $filter:", $@);
}
}
return $text;
}
sub fatal {
my $stack = shift;
my $filename = $stack->{'variables'}->{'FILE'};
local $" = ' ';
print STDERR "$0:$filename:$.: @_\n";
exit(1);
}
########################################################################
package stack;
# condition evaluated just prior to this context was false
use constant COND_FALSE => 0;
# condition evaluated just prior to this context was true
use constant COND_TRUE => 1;
# some prior condition at this level already evaluated to true (or a
# parent condition evaluated to false or must be ignored), so we're
# ignoring all remaining conditions at current level (and nested
# conditions, too)
use constant COND_COMPLETED => 2;
sub new {
return bless {
'variables' => {
# %ENV,
'LINE' => 0, # the line number in the source file
'DIRECTORY' => '', # current directory
'FILE' => '', # source filename
'1' => 1, # for convenience (the constant '1' is thus true)
},
'filters' => {
# filters
},
'values' => [], # the value of the last condition evaluated at the nth level
'lastConditionState' => [], # whether the condition in the nth-level context was true, false, or not applicable
'conditionState' => COND_TRUE,
'dependencies' => 0, # whether we are showing dependencies
'lineEndings' => "\n", # default to platform conventions
};
}
sub newline {
my $self = shift;
$self->{'variables'}->{'LINE'}++;
}
sub define {
my $self = shift;
my($variable, $value) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
$self->{'variables'}->{$variable} = $value;
}
sub defined {
my $self = shift;
my($variable) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
return defined($self->{'variables'}->{$variable});
}
sub undefine {
my $self = shift;
my($variable) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
delete($self->{'variables'}->{$variable});
}
sub get {
my $self = shift;
my($variable, $required) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
my $value = $self->{'variables'}->{$variable};
if (defined($value)) {
return $value;
} else {
die "variable '$variable' is not defined\n" if $required;
return '';
}
}
sub replace {
my $self = shift;
my ($value) = @_;
${$self->{'values'}}[-1] = $value;
$self->{'conditionState'} = $self->{'conditionState'} != COND_FALSE
? COND_COMPLETED
: $value ? COND_TRUE : COND_FALSE;
}
sub push {
my $self = shift;
my($value) = @_;
push(@{$self->{'values'}}, $value);
my $lastCondition = $self->{'conditionState'};
push(@{$self->{'lastConditionState'}}, $lastCondition);
$self->{'conditionState'} = $lastCondition != COND_TRUE
? COND_COMPLETED
: $value ? COND_TRUE : COND_FALSE;
}
sub pop {
my $self = shift;
$self->{'conditionState'} = pop(@{$self->{'lastConditionState'}});
return pop(@{$self->{'values'}});
}
sub enabled {
my $self = shift;
return $self->{'conditionState'} == COND_TRUE;
}
sub disabled {
my $self = shift;
return $self->{'conditionState'} != COND_TRUE;
}
sub filter {
my $self = shift;
my($filter, $value) = @_;
die "not a valid filter name: '$filter'\n" if $filter =~ m/\W/;
$self->{'filters'}->{$filter} = $value;
}
sub expand {
my $self = shift;
my($line) = @_;
$line =~ s/__(\w+)__/$self->get($1)/gose;
return $line;
}
sub print {
my $self = shift;
return if $self->{'dependencies'};
foreach my $line (@_) {
if (chomp $line) {
CORE::print("$line$self->{'lineEndings'}");
} else {
CORE::print($line);
}
}
}
sub visit {
my $self = shift;
my($filename) = @_;
my $directory = $stack->{'variables'}->{'DIRECTORY'};
$filename = File::Spec::_0_8::abs2rel(File::Spec::_0_8::rel2abs($filename, $directory));
CORE::print("$filename\n");
}
########################################################################
package preprocessor;
sub define {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $argument = shift;
for ($argument) {
/^(\w+)\s(.*)$/os && do {
return $stack->define($1, $2);
};
/^(\w+)$/os && do {
return $stack->define($1, 1);
};
die "invalid argument: '$_'\n";
}
}
sub undef {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
$stack->undefine(@_);
}
sub ifdef {
my $stack = shift;
my $variable = shift;
my $replace = defined(shift);
die "argument expected\n" unless defined($variable);
if ($replace) {
$stack->replace($stack->defined($variable));
} else {
$stack->push($stack->defined($variable));
}
}
sub ifndef {
my $stack = shift;
my $variable = shift;
my $replace = defined(shift);
die "argument expected\n" unless defined($variable);
if ($replace) {
$stack->replace(not $stack->defined($variable));
} else {
$stack->push(not $stack->defined($variable));
}
}
sub if {
my $stack = shift;
die "argument expected\n" unless @_;
my $argument = shift;
my $replace = defined(shift);
for ($argument) {
/^(\w+)==(.*)$/os && do {
# equality
if ($replace) {
return $stack->replace($stack->get($1) eq $2);
} else {
return $stack->push($stack->get($1) eq $2);
}
};
/^(\w+)!=(.*)$/os && do {
# inequality
if ($replace) {
return $stack->replace($stack->get($1) ne $2);
} else {
return $stack->push($stack->get($1) ne $2);
}
};
/^(\w+)$/os && do {
# true value
if ($replace) {
return $stack->replace($stack->get($1));
} else {
return $stack->push($stack->get($1));
}
};
/^!(\w+)$/os && do {
# false value
if ($replace) {
return $stack->replace(not $stack->get($1));
} else {
return $stack->push(not $stack->get($1));
}
};
die "invalid argument: '$_'\n";
}
}
sub else {
my $stack = shift;
die "argument unexpected\n" if @_;
$stack->replace(1);
}
sub elif {
my $stack = shift;
die "argument expected\n" unless @_;
&if($stack, @_, 1);
}
sub elifdef {
my $stack = shift;
die "argument expected\n" unless @_;
&ifdef($stack, @_, 1);
}
sub elifndef {
my $stack = shift;
die "argument expected\n" unless @_;
&ifndef($stack, @_, 1);
}
sub endif {
my $stack = shift;
die "argument unexpected\n" if @_;
$stack->pop;
}
sub error {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = $stack->expand(@_);
die "$line\n";
}
sub expand {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = $stack->expand(@_);
$stack->print("$line\n");
}
sub literal {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = shift;
$stack->print("$line\n");
}
sub include {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath(@_));
if ($stack->{'dependencies'}) {
$stack->visit($filename);
} else {
main::include($stack, $filename);
}
}
sub includesubst {
my ($stack, $filename) = @_;
return if $stack->disabled;
die "argument expected\n" unless $filename;
$filename =~ s/@(\w+)@/$stack->get($1, 1)/gose;
$filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath($filename));
if ($stack->{'dependencies'}) {
$stack->visit($filename);
} else {
main::include($stack, $filename);
}
}
sub filter {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
foreach (split(/\s/os, shift)) {
$stack->filter($_, 1);
}
}
sub unfilter {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
foreach (split(/\s/os, shift)) {
$stack->filter($_, 0);
}
}
########################################################################
package filter;
sub emptyLines {
my($stack, $text) = @_;
$text = "" if $text eq "\n";
return $text;
}
sub spaces {
my($stack, $text) = @_;
$text =~ s/ +/ /gos; # middle spaces
$text =~ s/^ //gos; # start spaces
$text =~ s/ (\n?)$/$1/gos; # end spaces
return $text;
}
sub slashslash {
my($stack, $text) = @_;
$text =~ s|//.*?(\n?)$|$1|gos;
return $text;
}
sub substitution {
my($stack, $text) = @_;
$text =~ s/@(\w+)@/$stack->get($1, 1)/gose;
return $text;
}
sub attemptSubstitution {
my($stack, $text) = @_;
$text =~ s/@(\w+)@/$stack->get($1, 0)/gose;
return $text;
}
########################################################################
########################################################################
# This code is from File::Spec::Unix 0.8.
# It is not considered a part of the preprocessor.pl source file
# This code is licensed under the same license as File::Spec itself.
package File::Spec::_0_8;
use Cwd;
sub rel2abs {
my ($path, $base) = @_;
if ( ! File::Spec->file_name_is_absolute( $path ) ) {
if ( !defined( $base ) || $base eq '' ) {
$base = cwd() ;
} elsif ( ! File::Spec->file_name_is_absolute( $base ) ) {
$base = rel2abs( $base );
} else {
$base = File::Spec->canonpath( $base );
}
$path = File::Spec->catdir( $base, $path );
}
return File::Spec->canonpath( $path );
}
sub splitdir {
return split m|/|, $_[1], -1; # Preserve trailing fields
}
sub splitpath {
my ($path, $nofile) = @_;
my ($volume,$directory,$file) = ('','','');
if ( $nofile ) {
$directory = $path;
}
else {
$path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs;
$directory = $1;
$file = $2;
}
return ($volume,$directory,$file);
}
sub catpath {
my ($volume,$directory,$file) = @_;
if ( $directory ne '' &&
$file ne '' &&
substr( $directory, -1 ) ne '/' &&
substr( $file, 0, 1 ) ne '/'
) {
$directory .= "/$file" ;
}
else {
$directory .= $file ;
}
return $directory ;
}
sub abs2rel {
my($path,$base) = @_;
# Clean up $path
if ( ! File::Spec->file_name_is_absolute( $path ) ) {
$path = rel2abs( $path ) ;
}
else {
$path = File::Spec->canonpath( $path ) ;
}
# Figure out the effective $base and clean it up.
if ( !defined( $base ) || $base eq '' ) {
$base = cwd();
}
elsif ( ! File::Spec->file_name_is_absolute( $base ) ) {
$base = rel2abs( $base ) ;
}
else {
$base = File::Spec->canonpath( $base ) ;
}
# Now, remove all leading components that are the same
my @pathchunks = File::Spec::_0_8::splitdir( $path);
my @basechunks = File::Spec::_0_8::splitdir( $base);
while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) {
shift @pathchunks ;
shift @basechunks ;
}
$path = CORE::join( '/', @pathchunks );
$base = CORE::join( '/', @basechunks );
# $base now contains the directories the resulting relative path
# must ascend out of before it can descend to $path_directory. So,
# replace all names with $parentDir
$base =~ s|[^/]+|..|g ;
# Glue the two together, using a separator if necessary, and preventing an
# empty result.
if ( $path ne '' && $base ne '' ) {
$path = "$base/$path" ;
} else {
$path = "$base$path" ;
}
return File::Spec->canonpath( $path ) ;
}
# End code from File::Spec::Unix 0.8.
########################################################################

View File

@ -1,252 +0,0 @@
Preprocessor
============
This is a very primitive line based preprocessor, for times when using
a C preprocessor isn't an option.
Instructions
------------
Any line starting with a hash # and a letter is considered to be a
preprocessor instruction. Other lines starting with a hash are ignored
as comments.
The following preprocessor instructions are recognised.
#define VARIABLE
#define VARIABLE STRING
#undef VARIABLE
#ifdef VARIABLE
#ifndef VARIABLE
#if VARIABLE
#if !VARIABLE
#if VARIABLE==STRING
#if VARIABLE!=STRING
#else
#elifdef VARIABLE
#elifndef VARIABLE
#elif VARIABLE
#elif !VARIABLE
#elif VARIABLE==STRING
#elif VARIABLE!=STRING
#endif
#error STRING
#include FILENAME
#includesubst @VAR@FILENAME
#expand STRING
#literal STRING
#filter FILTER1 FILTER2 ... FILTERn
#unfilter FILTER1 FILTER2 ... FILTERn
Whitespace is significant -- for instance, '#define TEST foo' is not
the same as '#define TEST foo '. The first defines TEST to be a three
character string, the second defines it to be four characters long.
The conditionals (#ifdef, #ifndef, #if, #else, #elifdef, #elifndef,
#elif, #endif) can be nested to arbitrary depth.
The #elifdef, #elifndef, and #elif instructions are equivalent to
#else instructions combined with the relevant conditional. For
example,
#ifdef foo
block 1
#elifdef bar
block 2
#endif
...could be written as:
#ifdef foo
block 1
#else
#ifdef bar
block 2
#endif
#endif
An #else block is included if all previous conditions were false, and
is equivalent to #elif 1, i.e.:
#ifdef foo
foo is defined
#else
foo is not defined
#endif
...is equivalent to:
#ifdef foo
foo is defined
#elif 1
foo is not defined
#endif
#else is not required to be the last condition in an if/el*/endif
series. In particular, along with #else's equivalence to #elif 1
this means that the following holds:
#if 0
never included
#else
always included
#else
never included
#elif 1
never included
#endif
The #error instruction stops execution at this point with a fatal
error. The error message will include the given STRING.
The #include instruction causes the specified file FILENAME to be
recursively processed, as if it was inserted at the current position
in the file. This means conditionals can be started in one file and
ended in another, although this practice is strongly discouraged.
There is no predefined limit to the depth of #includes, and there is
no restriction on self-inclusion, so care should be taken to avoid
infinite loops.
The #includesubst instruction behaves like #include, except that any
variables in @ATSIGNS@ are expanded, like the substitution filter.
The #expand instruction will print the given STRING with variable
substitutions. See the substitution section below.
The #literal instruction will print the given STRING with a newline,
with absolutely no other fixups, guaranteed. This can be used to
output lines starting with a #, which would otherwise be stripped out
as comments.
The #filter instruction enables the specified filters. You can turn
off filters using #unfilter. See the Filters section below.
Variables
---------
Variables consist of any alphanumeric string. They are defined using
the -D command line argument and the #define instruction.
To define all environment variables, so that you can use __HOME__,
etc, with #expand, use the -E argument. Note that arguments that use
non-word characters (like "!") are not included. (In particular,
cygwin is known to include all kinds of weird characters in its
environment variables.)
Two special variables are predefined, FILE and LINE. They can be
passed to #define and #undef, but FILE is automatically redefined at
the top of each file, and LINE is increased by one at the start of
each line.
The variable '1' is predefined with value 1. The variable '0' is not
defined. This allows constructs such as
#if 0
...
#endif
...to be used to quickly comment out large sections. Note, however,
that these are simply variables, and can be redefined. This is
strongly discouraged.
Substitution
------------
In any line starting with the instruction #expand, variable names
contained between double underscores, like __THIS__, are expanded to
their string values, or the empty string if they are not defined.
For example to print the current filename:
#expand <!-- This file is automatically generated from __FILE__ -->
Normal lines are not affected.
See also the substitution filter below.
Filters
-------
The following filters are supported:
emptyLines
Strips blank lines from the output.
slashslash
Strips everything from the first two consecutive slash (/)
characters until the end of the line.
spaces
Collapses sequences of spaces into a single space.
substitution
Replaces occurrences of "@foo@" by the value of the variable
"foo". If @foo@ is not defined, the preprocessor will terminate
with a fatal error.
attemptSubstitution
Replaces occurrences of "@foo@" by the value of the variable
"foo". If @foo@ is not defined, the empty string is used instead.
Filters are run in alphabetical order, on a per-line basis.
Command Line Arguments
----------------------
Syntax:
preprocessor.pl [-Dvariable[=value]] [-E] [-Ffilter]
[-Ifilename] [-d] [--marker=<c>] [--] filenames...
-Dvariable
Set variable to 1 before processing the files.
-Dvariable=value
Set variable to value before processing the files.
-E
Define all environment variables.
-Ffilter
Enables the specified filter.
-Ifilename
Include filename before any other files.
-d
Run through the files on the command line, listing the files they
depend on given the specified environment variables and filters.
Doesn't recurse into those files. The output is given as one
dependency per line, and filenames are given relative to the
current directory.
--line-endings=type
Set the type of line endings to use. "type" can be either "cr",
"lf", or "crlf". The default is whatever your platform uses for
perl's "\n" character.
--marker=<c>
Use the character <c> instead of '#' as the marker for preprocessor
instructions.
--
Indicates the end of non-filename arguments.
-
Indicates that input should come from standard input.
If no filenames are provided, standard input is used instead. If many
files are provided, they are processed sequentially, as if they were
one big file. -I files are handled before the other files, in the
order specified, but after handling any -D, -E, -F, and -d arguments.
Contact Details
---------------
Feel free to e-mail me if you have any questions:
Ian Hickson <preprocessor@software.hixie.ch>

209
js/src/config/Expression.py Normal file
View File

@ -0,0 +1,209 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla build system.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Axel Hecht <axel@pike.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
"""
Parses and evaluates simple statements for Preprocessor:
Expression currently supports the following grammar, whitespace is ignored:
expression :
unary ( ( '==' | '!=' ) unary ) ? ;
unary :
'!'? value ;
value :
[0-9]+ # integer
| \w+ # string identifier or value;
"""
import re
class Expression:
def __init__(self, expression_string):
"""
Create a new expression with this string.
The expression will already be parsed into an Abstract Syntax Tree.
"""
self.content = expression_string
self.offset = 0
self.__ignore_whitespace()
self.e = self.__get_equality()
if self.content:
raise Expression.ParseError, self
def __get_equality(self):
"""
Production: unary ( ( '==' | '!=' ) unary ) ?
"""
if not len(self.content):
return None
rv = Expression.__AST("equality")
# unary
rv.append(self.__get_unary())
self.__ignore_whitespace()
if not re.match('[=!]=', self.content):
# no equality needed, short cut to our prime unary
return rv[0]
# append operator
rv.append(Expression.__ASTLeaf('op', self.content[:2]))
self.__strip(2)
self.__ignore_whitespace()
rv.append(self.__get_unary())
self.__ignore_whitespace()
return rv
def __get_unary(self):
"""
Production: '!'? value
"""
# eat whitespace right away, too
not_ws = re.match('!\s*', self.content)
if not not_ws:
return self.__get_value()
rv = Expression.__AST('not')
self.__strip(not_ws.end())
rv.append(self.__get_value())
self.__ignore_whitespace()
return rv
def __get_value(self):
"""
Production: ( [0-9]+ | \w+)
Note that the order is important, and the expression is kind-of
ambiguous as \w includes 0-9. One could make it unambiguous by
removing 0-9 from the first char of a string literal.
"""
rv = None
word_len = re.match('[0-9]*', self.content).end()
if word_len:
value = int(self.content[:word_len])
rv = Expression.__ASTLeaf('int', value)
else:
word_len = re.match('\w*', self.content).end()
if word_len:
rv = Expression.__ASTLeaf('string', self.content[:word_len])
else:
raise Expression.ParseError, self
self.__strip(word_len)
self.__ignore_whitespace()
return rv
def __ignore_whitespace(self):
ws_len = re.match('\s*', self.content).end()
self.__strip(ws_len)
return
def __strip(self, length):
"""
Remove a given amount of chars from the input and update
the offset.
"""
self.content = self.content[length:]
self.offset += length
def evaluate(self, context):
"""
Evaluate the expression with the given context
"""
# Helper function to evaluate __get_equality results
def eval_equality(tok):
left = opmap[tok[0].type](tok[0])
right = opmap[tok[2].type](tok[2])
rv = left == right
if tok[1].value == '!=':
rv = not rv
return rv
# Mapping from token types to evaluator functions
# Apart from (non-)equality, all these can be simple lambda forms.
opmap = {
'equality': eval_equality,
'not': lambda tok: not opmap[tok[0].type](tok[0]),
'string': lambda tok: context[tok.value],
'int': lambda tok: tok.value}
return opmap[self.e.type](self.e);
class __AST(list):
"""
Internal class implementing Abstract Syntax Tree nodes
"""
def __init__(self, type):
self.type = type
super(self.__class__, self).__init__(self)
class __ASTLeaf:
"""
Internal class implementing Abstract Syntax Tree leafs
"""
def __init__(self, type, value):
self.value = value
self.type = type
def __str__(self):
return self.value.__str__()
def __repr__(self):
return self.value.__repr__()
class ParseError(StandardError):
"""
Error raised when parsing fails.
It has two members, offset and content, which give the offset of the
error and the offending content.
"""
def __init__(self, expression):
self.offset = expression.offset
self.content = expression.content[:3]
def __str__(self):
return 'Unexpected content at offset %i, "%s"'%(self.offset, self.content)
class Context(dict):
"""
This class holds variable values by subclassing dict, and while it
truthfully reports True and False on
name in context
it returns the variable name itself on
context["name"]
to reflect the ambiguity between string literals and preprocessor
variables.
"""
def __getitem__(self, key):
if key in self:
return super(self.__class__, self).__getitem__(key)
return key

View File

@ -93,7 +93,7 @@ endif
ifdef WRAP_SYSTEM_INCLUDES
export::
if test ! -d system_wrappers_js; then mkdir system_wrappers_js; fi
$(PERL) $(srcdir)/preprocessor.pl $(DEFINES) $(ACDEFINES) \
$(PYTHON) $(srcdir)/Preprocessor.py $(DEFINES) $(ACDEFINES) \
-DBUILD_STATIC_LIBS=$(BUILD_STATIC_LIBS) \
$(srcdir)/system-headers | $(PERL) $(srcdir)/make-system-wrappers.pl system_wrappers_js
$(INSTALL) system_wrappers_js $(DIST)

View File

@ -0,0 +1,477 @@
"""
This is a very primitive line based preprocessor, for times when using
a C preprocessor isn't an option.
"""
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla build system.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Axel Hecht <axel@pike.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import sys
import os
import os.path
import re
from optparse import OptionParser
# hack around win32 mangling our line endings
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65443
if sys.platform == "win32":
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
os.linesep = '\n'
import Expression
__all__ = ['Preprocessor', 'preprocess']
class Preprocessor:
"""
Class for preprocessing text files.
"""
class Error(RuntimeError):
def __init__(self, cpp, MSG, context):
self.file = cpp.context['FILE']
self.line = cpp.context['LINE']
self.key = MSG
RuntimeError.__init__(self, (self.file, self.line, self.key, context))
def __init__(self):
self.context = Expression.Context()
for k,v in {'FILE': '',
'LINE': 0,
'DIRECTORY': os.path.abspath('.')}.iteritems():
self.context[k] = v
self.disableLevel = 0
# ifStates can be
# 0: hadTrue
# 1: wantsTrue
# 2: #else found
self.ifStates = []
self.checkLineNumbers = False
self.writtenLines = 0
self.filters = []
self.cmds = {}
for cmd, level in {'define': 0,
'undef': 0,
'if': sys.maxint,
'ifdef': sys.maxint,
'ifndef': sys.maxint,
'else': 1,
'elif': 1,
'elifdef': 1,
'elifndef': 1,
'endif': sys.maxint,
'expand': 0,
'literal': 0,
'filter': 0,
'unfilter': 0,
'include': 0,
'includesubst': 0,
'error': 0}.iteritems():
self.cmds[cmd] = (level, getattr(self, 'do_' + cmd))
self.out = sys.stdout
self.setMarker('#')
self.LE = '\n'
self.varsubst = re.compile('@(?P<VAR>\w+)@', re.U)
def setLineEndings(self, aLE):
"""
Set the line endings to be used for output.
"""
self.LE = {'cr': '\x0D', 'lf': '\x0A', 'crlf': '\x0D\x0A'}[aLE]
def setMarker(self, aMarker):
"""
Set the marker to be used for processing directives.
Used for handling CSS files, with pp.setMarker('%'), for example.
"""
self.marker = aMarker
self.instruction = re.compile('%s(?P<cmd>[a-z]+)(?:\s(?P<args>.*))?$'%aMarker, re.U)
self.comment = re.compile(aMarker, re.U)
def clone(self):
"""
Create a clone of the current processor, including line ending
settings, marker, variable definitions, output stream.
"""
rv = Preprocessor()
rv.context.update(self.context)
rv.setMarker(self.marker)
rv.LE = self.LE
rv.out = self.out
return rv
def write(self, aLine):
"""
Internal method for handling output.
"""
if self.checkLineNumbers:
self.writtenLines += 1
ln = self.context['LINE']
if self.writtenLines != ln:
self.out.write('//@line %(line)d "%(file)s"%(le)s'%{'line': ln,
'file': self.context['FILE'],
'le': self.LE})
self.writtenLines = ln
for f in self.filters:
aLine = f[1](aLine)
# ensure our line ending. Only need to handle \n, as we're reading
# with universal line ending support, at least for files.
aLine = re.sub('\n', self.LE, aLine)
self.out.write(aLine)
def handleCommandLine(self, args, defaultToStdin = False):
"""
Parse a commandline into this parser.
Uses OptionParser internally, no args mean sys.argv[1:].
"""
p = self.getCommandLineParser()
(options, args) = p.parse_args(args=args)
includes = options.I
if defaultToStdin and len(args) == 0:
args = [sys.stdin]
includes.extend(args)
for f in includes:
self.do_include(f)
pass
def getCommandLineParser(self, unescapeDefines = False):
escapedValue = re.compile('".*"$')
numberValue = re.compile('\d+$')
def handleE(option, opt, value, parser):
for k,v in os.environ.iteritems():
self.context[k] = v
def handleD(option, opt, value, parser):
vals = value.split('=', 1)
if len(vals) == 1:
vals.append(1)
elif unescapeDefines and escapedValue.match(vals[1]):
# strip escaped string values
vals[1] = vals[1][1:-1]
elif numberValue.match(vals[1]):
vals[1] = int(vals[1])
self.context[vals[0]] = vals[1]
def handleU(option, opt, value, parser):
del self.context[value]
def handleF(option, opt, value, parser):
self.do_filter(value)
def handleLE(option, opt, value, parser):
self.setLineEndings(value)
def handleMarker(option, opt, value, parser):
self.setMarker(value)
p = OptionParser()
p.add_option('-I', action='append', type="string", default = [],
metavar="FILENAME", help='Include file')
p.add_option('-E', action='callback', callback=handleE,
help='Import the environment into the defined variables')
p.add_option('-D', action='callback', callback=handleD, type="string",
metavar="VAR[=VAL]", help='Define a variable')
p.add_option('-U', action='callback', callback=handleU, type="string",
metavar="VAR", help='Undefine a variable')
p.add_option('-F', action='callback', callback=handleF, type="string",
metavar="FILTER", help='Enable the specified filter')
p.add_option('--line-endings', action='callback', callback=handleLE,
type="string", metavar="[cr|lr|crlf]",
help='Use the specified line endings [Default: OS dependent]')
p.add_option('--marker', action='callback', callback=handleMarker,
type="string",
help='Use the specified marker instead of #')
return p
def handleLine(self, aLine):
"""
Handle a single line of input (internal).
"""
m = self.instruction.match(aLine)
if m:
args = None
cmd = m.group('cmd')
try:
args = m.group('args')
except IndexError:
pass
if cmd not in self.cmds:
raise Preprocessor.Error(self, 'INVALID_CMD', aLine)
level, cmd = self.cmds[cmd]
if (level >= self.disableLevel):
cmd(args)
elif self.disableLevel == 0 and not self.comment.match(aLine):
self.write(aLine)
pass
# Instruction handlers
# These are named do_'instruction name' and take one argument
# Variables
def do_define(self, args):
m = re.match('(?P<name>\w+)(?:\s(?P<value>.*))?', args, re.U)
if not m:
raise Preprocessor.Error(self, 'SYNTAX_DEF', args)
val = 1
if m.group('value'):
val = m.group('value')
try:
val = int(val)
except:
pass
self.context[m.group('name')] = val
def do_undef(self, args):
m = re.match('(?P<name>\w+)$', args, re.U)
if not m:
raise Preprocessor.Error(self, 'SYNTAX_DEF', args)
if args in self.context:
del self.context[args]
# Logic
def ensure_not_else(self):
if len(self.ifStates) == 0 or self.ifStates[-1] == 2:
sys.stderr.write('WARNING: bad nesting of #else\n')
def do_if(self, args, replace=False):
if self.disableLevel and not replace:
self.disableLevel += 1
return
val = None
try:
e = Expression.Expression(args)
val = e.evaluate(self.context)
except Exception:
# XXX do real error reporting
raise Preprocessor.Error(self, 'SYNTAX_ERR', args)
if type(val) == str:
# we're looking for a number value, strings are false
val = False
if not val:
self.disableLevel = 1
if replace:
if val:
self.disableLevel = 0
self.ifStates[-1] = self.disableLevel
else:
self.ifStates.append(self.disableLevel)
pass
def do_ifdef(self, args, replace=False):
if self.disableLevel and not replace:
self.disableLevel += 1
return
if re.match('\W', args, re.U):
raise Preprocessor.Error(self, 'INVALID_VAR', args)
if args not in self.context:
self.disableLevel = 1
if replace:
if args in self.context:
self.disableLevel = 0
self.ifStates[-1] = self.disableLevel
else:
self.ifStates.append(self.disableLevel)
pass
def do_ifndef(self, args, replace=False):
if self.disableLevel and not replace:
self.disableLevel += 1
return
if re.match('\W', args, re.U):
raise Preprocessor.Error(self, 'INVALID_VAR', args)
if args in self.context:
self.disableLevel = 1
if replace:
if args not in self.context:
self.disableLevel = 0
self.ifStates[-1] = self.disableLevel
else:
self.ifStates.append(self.disableLevel)
pass
def do_else(self, args, ifState = 2):
self.ensure_not_else()
hadTrue = self.ifStates[-1] == 0
self.ifStates[-1] = ifState # in-else
if hadTrue:
self.disableLevel = 1
return
self.disableLevel = 0
def do_elif(self, args):
if self.disableLevel == 1:
if self.ifStates[-1] == 1:
self.do_if(args, replace=True)
else:
self.do_else(None, self.ifStates[-1])
def do_elifdef(self, args):
if self.disableLevel == 1:
if self.ifStates[-1] == 1:
self.do_ifdef(args, replace=True)
else:
self.do_else(None, self.ifStates[-1])
def do_elifndef(self, args):
if self.disableLevel == 1:
if self.ifStates[-1] == 1:
self.do_ifndef(args, replace=True)
else:
self.do_else(None, self.ifStates[-1])
def do_endif(self, args):
if self.disableLevel > 0:
self.disableLevel -= 1
if self.disableLevel == 0:
self.ifStates.pop()
# output processing
def do_expand(self, args):
lst = re.split('__(\w+)__', args, re.U)
do_replace = False
def vsubst(v):
if v in self.context:
return str(self.context[v])
return ''
for i in range(1, len(lst), 2):
lst[i] = vsubst(lst[i])
lst.append('\n') # add back the newline
self.write(reduce(lambda x, y: x+y, lst, ''))
def do_literal(self, args):
self.write(args + self.LE)
def do_filter(self, args):
filters = [f for f in args.split(' ') if hasattr(self, 'filter_' + f)]
if len(filters) == 0:
return
current = dict(self.filters)
for f in filters:
current[f] = getattr(self, 'filter_' + f)
filterNames = current.keys()
filterNames.sort()
self.filters = [(fn, current[fn]) for fn in filterNames]
return
def do_unfilter(self, args):
filters = args.split(' ')
current = dict(self.filters)
for f in filters:
if f in current:
del current[f]
filterNames = current.keys()
filterNames.sort()
self.filters = [(fn, current[fn]) for fn in filterNames]
return
# Filters
#
# emptyLines
# Strips blank lines from the output.
def filter_emptyLines(self, aLine):
if aLine == '\n':
return ''
return aLine
# slashslash
# Strips everything after //
def filter_slashslash(self, aLine):
[aLine, rest] = aLine.split('//', 1)
if rest:
aLine += '\n'
return aLine
# spaces
# Collapses sequences of spaces into a single space
def filter_spaces(self, aLine):
return re.sub(' +', ' ', aLine).strip(' ')
# substition
# helper to be used by both substition and attemptSubstitution
def filter_substitution(self, aLine, fatal=True):
def repl(matchobj):
varname = matchobj.group('VAR')
if varname in self.context:
return str(self.context[varname])
if fatal:
raise Preprocessor.Error(self, 'UNDEFINED_VAR', varname)
return ''
return self.varsubst.sub(repl, aLine)
def filter_attemptSubstitution(self, aLine):
return self.filter_substitution(aLine, fatal=False)
# File ops
def do_include(self, args):
"""
Preprocess a given file.
args can either be a file name, or a file-like object.
Files should be opened, and will be closed after processing.
"""
isName = type(args) == str or type(args) == unicode
oldWrittenLines = self.writtenLines
oldCheckLineNumbers = self.checkLineNumbers
self.checkLineNumbers = False
if isName:
try:
args = str(args)
if not os.path.isabs(args):
args = os.path.join(self.context['DIRECTORY'], args)
args = open(args, 'rU')
except:
raise Preprocessor.Error(self, 'FILE_NOT_FOUND', str(args))
self.checkLineNumbers = bool(re.search('\.js(?:\.in)?$', args.name))
oldFile = self.context['FILE']
oldLine = self.context['LINE']
oldDir = self.context['DIRECTORY']
if args.isatty():
# we're stdin, use '-' and '' for file and dir
self.context['FILE'] = '-'
self.context['DIRECTORY'] = ''
else:
abspath = os.path.abspath(args.name)
self.context['FILE'] = abspath
self.context['DIRECTORY'] = os.path.dirname(abspath)
self.context['LINE'] = 0
self.writtenLines = 0
for l in args:
self.context['LINE'] += 1
self.handleLine(l)
args.close()
self.context['FILE'] = oldFile
self.checkLineNumbers = oldCheckLineNumbers
self.writtenLines = oldWrittenLines
self.context['LINE'] = oldLine
self.context['DIRECTORY'] = oldDir
def do_includesubst(self, args):
args = self.filter_substitution(args)
self.do_include(args)
def do_error(self, args):
raise Preprocessor.Error(self, 'Error: ', str(args))
def main():
pp = Preprocessor()
pp.handleCommandLine(None, True)
return
def preprocess(includes=[sys.stdin], defines={},
output = sys.stdout,
line_endings='\n', marker='#'):
pp = Preprocessor()
pp.context.update(defines)
pp.setLineEndings(line_endings)
pp.setMarker(marker)
pp.out = output
for f in includes:
pp.do_include(f)
if __name__ == "__main__":
main()

View File

@ -1,671 +0,0 @@
#!/usr/bin/perl -w
# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
#
# Preprocessor
# Version 1.1
#
# Copyright (c) 2002, 2003, 2004 by Ian Hickson
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Thanks to bryner and bsmedberg for suggestions.
# Thanks to jon rekai for a patch to not require File::Spec 0.8.
use strict;
# takes as arguments the files to process
# defaults to stdin
# output to stdout
my $stack = new stack;
my $marker = '#';
# command line arguments
my @includes;
while ($_ = $ARGV[0], defined($_) && /^-./) {
shift;
last if /^--$/os;
if (/^-D(.*)$/os) {
for ($1) {
if (/^([\w\.]+)=(.*)$/os) {
$stack->define($1, $2);
} elsif (/^([\w\.]+)$/os) {
$stack->define($1, 1);
} else {
die "$0: invalid argument to -D: $_\n";
}
}
} elsif (/^-F(.*)$/os) {
for ($1) {
if (/^(\w+)$/os) {
$stack->filter($1, 1);
} else {
die "$0: invalid argument to -F: $_\n";
}
}
} elsif (/^-I(.*)$/os) {
push(@includes, $1);
} elsif (/^-E$/os) {
foreach (keys %ENV) {
# define all variables that have valid names
$stack->define($_, $ENV{$_}) unless m/\W/;
}
} elsif (/^-d$/os) {
$stack->{'dependencies'} = 1;
} elsif (/^--line-endings=crlf$/os) {
$stack->{'lineEndings'} = "\x0D\x0A";
} elsif (/^--line-endings=cr$/os) {
$stack->{'lineEndings'} = "\x0D";
} elsif (/^--line-endings=lf$/os) {
$stack->{'lineEndings'} = "\x0A";
} elsif (/^--line-endings=(.+)$/os) {
die "$0: unrecognised line ending: $1\n";
} elsif (/^--marker=(.)$/os) {
$marker = $1;
} else {
die "$0: invalid argument: $_\n";
}
}
unshift(@ARGV, '-') unless @ARGV;
unshift(@ARGV, @includes);
# do the work
foreach (@ARGV) { include($stack, $_); }
exit(0);
########################################################################
package main;
use File::Spec;
use File::Spec::Unix; # on all platforms, because the #include syntax is unix-based
# Note: Ideally we would use File::Spec 0.8. When this becomes
# possible, add "0.8" to the first "use" line above, then replace
# occurrences of "::_0_8::" with "->" below. And remove the code for
# File::Spec 0.8 much lower down the file.
sub include {
my($stack, $filename) = @_;
my $directory = $stack->{'variables'}->{'DIRECTORY'};
if ($filename ne '-') {
$filename = File::Spec::_0_8::rel2abs($filename, $directory);
# splitpath expects forward-slash paths on windows, so we have to
# change the slashes if using Activestate Perl.
$filename =~ s?\\?/?g if "$^O" eq "MSWin32";
my($volume, $path) = File::Spec::_0_8::splitpath($filename);
$directory = File::Spec::_0_8::catpath($volume, $path, '');
}
local $stack->{'variables'}->{'DIRECTORY'} = $directory;
local $stack->{'variables'}->{'FILE'} = $filename;
local $stack->{'variables'}->{'LINE'} = 0;
local *FILE;
open(FILE, $filename) or die "Couldn't open $filename: $!\n";
my $lineout = 0;
while (<FILE>) {
# on cygwin, line endings are screwed up, so normalise them.
s/[\x0D\x0A]+$/\n/os if ($^O eq 'msys' || $^O eq 'cygwin' || "$^O" eq "MSWin32");
$stack->newline;
if (/^\Q$marker\E([a-z]+)\n?$/os) { # argumentless processing instruction
process($stack, $1);
} elsif (/^\Q$marker\E([a-z]+)\s(.*?)\n?$/os) { # processing instruction with arguments
process($stack, $1, $2);
} elsif (/^\Q$marker\E/os) { # comment
# ignore it
} elsif ($stack->enabled) {
next if $stack->{'dependencies'};
# set the current line number in JavaScript if necessary
my $linein = $stack->{'variables'}->{'LINE'};
if (++$lineout != $linein) {
if ($filename =~ /\.js(|\.in)$/o) {
$stack->print("//\@line $linein \"$filename\"\n")
}
$lineout = $linein;
}
# print it, including any newlines
$stack->print(filtered($stack, $_));
}
}
close(FILE);
}
sub process {
my($stack, $instruction, @arguments) = @_;
my $method = 'preprocessor'->can($instruction);
if (not defined($method)) {
fatal($stack, 'unknown instruction', $instruction);
}
eval { &$method($stack, @arguments) };
if ($@) {
fatal($stack, "error evaluating $instruction:", $@);
}
}
sub filtered {
my($stack, $text) = @_;
foreach my $filter (sort keys %{$stack->{'filters'}}) {
next unless $stack->{'filters'}->{$filter};
my $method = 'filter'->can($filter);
if (not defined($method)) {
fatal($stack, 'unknown filter', $filter);
}
$text = eval { &$method($stack, $text) };
if ($@) {
fatal($stack, "error using $filter:", $@);
}
}
return $text;
}
sub fatal {
my $stack = shift;
my $filename = $stack->{'variables'}->{'FILE'};
local $" = ' ';
print STDERR "$0:$filename:$.: @_\n";
exit(1);
}
########################################################################
package stack;
# condition evaluated just prior to this context was false
use constant COND_FALSE => 0;
# condition evaluated just prior to this context was true
use constant COND_TRUE => 1;
# some prior condition at this level already evaluated to true (or a
# parent condition evaluated to false or must be ignored), so we're
# ignoring all remaining conditions at current level (and nested
# conditions, too)
use constant COND_COMPLETED => 2;
sub new {
return bless {
'variables' => {
# %ENV,
'LINE' => 0, # the line number in the source file
'DIRECTORY' => '', # current directory
'FILE' => '', # source filename
'1' => 1, # for convenience (the constant '1' is thus true)
},
'filters' => {
# filters
},
'values' => [], # the value of the last condition evaluated at the nth level
'lastConditionState' => [], # whether the condition in the nth-level context was true, false, or not applicable
'conditionState' => COND_TRUE,
'dependencies' => 0, # whether we are showing dependencies
'lineEndings' => "\n", # default to platform conventions
};
}
sub newline {
my $self = shift;
$self->{'variables'}->{'LINE'}++;
}
sub define {
my $self = shift;
my($variable, $value) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
$self->{'variables'}->{$variable} = $value;
}
sub defined {
my $self = shift;
my($variable) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
return defined($self->{'variables'}->{$variable});
}
sub undefine {
my $self = shift;
my($variable) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
delete($self->{'variables'}->{$variable});
}
sub get {
my $self = shift;
my($variable, $required) = @_;
die "not a valid variable name: '$variable'\n" if $variable =~ m/[^\w\.]/;
my $value = $self->{'variables'}->{$variable};
if (defined($value)) {
return $value;
} else {
die "variable '$variable' is not defined\n" if $required;
return '';
}
}
sub replace {
my $self = shift;
my ($value) = @_;
${$self->{'values'}}[-1] = $value;
$self->{'conditionState'} = $self->{'conditionState'} != COND_FALSE
? COND_COMPLETED
: $value ? COND_TRUE : COND_FALSE;
}
sub push {
my $self = shift;
my($value) = @_;
push(@{$self->{'values'}}, $value);
my $lastCondition = $self->{'conditionState'};
push(@{$self->{'lastConditionState'}}, $lastCondition);
$self->{'conditionState'} = $lastCondition != COND_TRUE
? COND_COMPLETED
: $value ? COND_TRUE : COND_FALSE;
}
sub pop {
my $self = shift;
$self->{'conditionState'} = pop(@{$self->{'lastConditionState'}});
return pop(@{$self->{'values'}});
}
sub enabled {
my $self = shift;
return $self->{'conditionState'} == COND_TRUE;
}
sub disabled {
my $self = shift;
return $self->{'conditionState'} != COND_TRUE;
}
sub filter {
my $self = shift;
my($filter, $value) = @_;
die "not a valid filter name: '$filter'\n" if $filter =~ m/\W/;
$self->{'filters'}->{$filter} = $value;
}
sub expand {
my $self = shift;
my($line) = @_;
$line =~ s/__(\w+)__/$self->get($1)/gose;
return $line;
}
sub print {
my $self = shift;
return if $self->{'dependencies'};
foreach my $line (@_) {
if (chomp $line) {
CORE::print("$line$self->{'lineEndings'}");
} else {
CORE::print($line);
}
}
}
sub visit {
my $self = shift;
my($filename) = @_;
my $directory = $stack->{'variables'}->{'DIRECTORY'};
$filename = File::Spec::_0_8::abs2rel(File::Spec::_0_8::rel2abs($filename, $directory));
CORE::print("$filename\n");
}
########################################################################
package preprocessor;
sub define {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $argument = shift;
for ($argument) {
/^(\w+)\s(.*)$/os && do {
return $stack->define($1, $2);
};
/^(\w+)$/os && do {
return $stack->define($1, 1);
};
die "invalid argument: '$_'\n";
}
}
sub undef {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
$stack->undefine(@_);
}
sub ifdef {
my $stack = shift;
my $variable = shift;
my $replace = defined(shift);
die "argument expected\n" unless defined($variable);
if ($replace) {
$stack->replace($stack->defined($variable));
} else {
$stack->push($stack->defined($variable));
}
}
sub ifndef {
my $stack = shift;
my $variable = shift;
my $replace = defined(shift);
die "argument expected\n" unless defined($variable);
if ($replace) {
$stack->replace(not $stack->defined($variable));
} else {
$stack->push(not $stack->defined($variable));
}
}
sub if {
my $stack = shift;
die "argument expected\n" unless @_;
my $argument = shift;
my $replace = defined(shift);
for ($argument) {
/^(\w+)==(.*)$/os && do {
# equality
if ($replace) {
return $stack->replace($stack->get($1) eq $2);
} else {
return $stack->push($stack->get($1) eq $2);
}
};
/^(\w+)!=(.*)$/os && do {
# inequality
if ($replace) {
return $stack->replace($stack->get($1) ne $2);
} else {
return $stack->push($stack->get($1) ne $2);
}
};
/^(\w+)$/os && do {
# true value
if ($replace) {
return $stack->replace($stack->get($1));
} else {
return $stack->push($stack->get($1));
}
};
/^!(\w+)$/os && do {
# false value
if ($replace) {
return $stack->replace(not $stack->get($1));
} else {
return $stack->push(not $stack->get($1));
}
};
die "invalid argument: '$_'\n";
}
}
sub else {
my $stack = shift;
die "argument unexpected\n" if @_;
$stack->replace(1);
}
sub elif {
my $stack = shift;
die "argument expected\n" unless @_;
&if($stack, @_, 1);
}
sub elifdef {
my $stack = shift;
die "argument expected\n" unless @_;
&ifdef($stack, @_, 1);
}
sub elifndef {
my $stack = shift;
die "argument expected\n" unless @_;
&ifndef($stack, @_, 1);
}
sub endif {
my $stack = shift;
die "argument unexpected\n" if @_;
$stack->pop;
}
sub error {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = $stack->expand(@_);
die "$line\n";
}
sub expand {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = $stack->expand(@_);
$stack->print("$line\n");
}
sub literal {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $line = shift;
$stack->print("$line\n");
}
sub include {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
my $filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath(@_));
if ($stack->{'dependencies'}) {
$stack->visit($filename);
} else {
main::include($stack, $filename);
}
}
sub includesubst {
my ($stack, $filename) = @_;
return if $stack->disabled;
die "argument expected\n" unless $filename;
$filename =~ s/@(\w+)@/$stack->get($1, 1)/gose;
$filename = File::Spec::_0_8::catpath(File::Spec::_0_8::splitpath($filename));
if ($stack->{'dependencies'}) {
$stack->visit($filename);
} else {
main::include($stack, $filename);
}
}
sub filter {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
foreach (split(/\s/os, shift)) {
$stack->filter($_, 1);
}
}
sub unfilter {
my $stack = shift;
return if $stack->disabled;
die "argument expected\n" unless @_;
foreach (split(/\s/os, shift)) {
$stack->filter($_, 0);
}
}
########################################################################
package filter;
sub emptyLines {
my($stack, $text) = @_;
$text = "" if $text eq "\n";
return $text;
}
sub spaces {
my($stack, $text) = @_;
$text =~ s/ +/ /gos; # middle spaces
$text =~ s/^ //gos; # start spaces
$text =~ s/ (\n?)$/$1/gos; # end spaces
return $text;
}
sub slashslash {
my($stack, $text) = @_;
$text =~ s|//.*?(\n?)$|$1|gos;
return $text;
}
sub substitution {
my($stack, $text) = @_;
$text =~ s/@(\w+)@/$stack->get($1, 1)/gose;
return $text;
}
sub attemptSubstitution {
my($stack, $text) = @_;
$text =~ s/@(\w+)@/$stack->get($1, 0)/gose;
return $text;
}
########################################################################
########################################################################
# This code is from File::Spec::Unix 0.8.
# It is not considered a part of the preprocessor.pl source file
# This code is licensed under the same license as File::Spec itself.
package File::Spec::_0_8;
use Cwd;
sub rel2abs {
my ($path, $base) = @_;
if ( ! File::Spec->file_name_is_absolute( $path ) ) {
if ( !defined( $base ) || $base eq '' ) {
$base = cwd() ;
} elsif ( ! File::Spec->file_name_is_absolute( $base ) ) {
$base = rel2abs( $base );
} else {
$base = File::Spec->canonpath( $base );
}
$path = File::Spec->catdir( $base, $path );
}
return File::Spec->canonpath( $path );
}
sub splitdir {
return split m|/|, $_[1], -1; # Preserve trailing fields
}
sub splitpath {
my ($path, $nofile) = @_;
my ($volume,$directory,$file) = ('','','');
if ( $nofile ) {
$directory = $path;
}
else {
$path =~ m|^ ( (?: .* / (?: \.\.?\Z(?!\n) )? )? ) ([^/]*) |xs;
$directory = $1;
$file = $2;
}
return ($volume,$directory,$file);
}
sub catpath {
my ($volume,$directory,$file) = @_;
if ( $directory ne '' &&
$file ne '' &&
substr( $directory, -1 ) ne '/' &&
substr( $file, 0, 1 ) ne '/'
) {
$directory .= "/$file" ;
}
else {
$directory .= $file ;
}
return $directory ;
}
sub abs2rel {
my($path,$base) = @_;
# Clean up $path
if ( ! File::Spec->file_name_is_absolute( $path ) ) {
$path = rel2abs( $path ) ;
}
else {
$path = File::Spec->canonpath( $path ) ;
}
# Figure out the effective $base and clean it up.
if ( !defined( $base ) || $base eq '' ) {
$base = cwd();
}
elsif ( ! File::Spec->file_name_is_absolute( $base ) ) {
$base = rel2abs( $base ) ;
}
else {
$base = File::Spec->canonpath( $base ) ;
}
# Now, remove all leading components that are the same
my @pathchunks = File::Spec::_0_8::splitdir( $path);
my @basechunks = File::Spec::_0_8::splitdir( $base);
while (@pathchunks && @basechunks && $pathchunks[0] eq $basechunks[0]) {
shift @pathchunks ;
shift @basechunks ;
}
$path = CORE::join( '/', @pathchunks );
$base = CORE::join( '/', @basechunks );
# $base now contains the directories the resulting relative path
# must ascend out of before it can descend to $path_directory. So,
# replace all names with $parentDir
$base =~ s|[^/]+|..|g ;
# Glue the two together, using a separator if necessary, and preventing an
# empty result.
if ( $path ne '' && $base ne '' ) {
$path = "$base/$path" ;
} else {
$path = "$base$path" ;
}
return File::Spec->canonpath( $path ) ;
}
# End code from File::Spec::Unix 0.8.
########################################################################

View File

@ -189,7 +189,7 @@ langpack-%: XPI_NAME=locale-$*
langpack-%: libs-%
@echo "Making langpack $(LANGPACK_FILE)"
$(NSINSTALL) -D $(DIST)/$(PKG_LANGPACK_PATH)
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) -I$(TK_DEFINES) -I$(APP_DEFINES) $(srcdir)/generic/install.rdf > $(FINAL_TARGET)/install.rdf
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) -I$(TK_DEFINES) -I$(APP_DEFINES) $(srcdir)/generic/install.rdf > $(FINAL_TARGET)/install.rdf
cd $(DIST)/xpi-stage/locale-$(AB_CD) && \
$(ZIP) -r9D $(LANGPACK_FILE) install.rdf chrome chrome.manifest -x chrome/$(AB_CD).manifest

View File

@ -212,7 +212,7 @@ endif
# XXX applications would need to supply this file
#export:: brand.dtd.in
# $(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $^ > brand.dtd
# $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > brand.dtd
export::
$(NSINSTALL) -D $(DIST)/branding

View File

@ -61,7 +61,7 @@ include $(topsrcdir)/config/rules.mk
libs:: stage-package
%.plist: %.plist.in
$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $< > $@
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $< > $@
PACKAGER_NO_LIBS=1
_APPNAME = XUL.framework