bug 361583, start using Preprocessor.py instead of preprocessor.pl in building Minefield, r=bsmedberg

This commit is contained in:
axel@pike.org 2007-04-11 09:35:01 -07:00
parent 84266dc3d0
commit a364c25975
18 changed files with 1142 additions and 22 deletions

View File

@ -50,7 +50,7 @@ FILES := \
$(NULL)
libs::
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $(srcdir)/install.rdf.in > install.rdf
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(srcdir)/install.rdf.in > install.rdf
$(INSTALL) $(FILES) $(DIST)/bin/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}
install::

View File

@ -47,9 +47,9 @@ DEFINES += -DAB_CD=$(AB_CD) -DMOZ_DISTRIBUTION_ID_UNQUOTED=$(MOZ_DISTRIBUTION_ID
include $(topsrcdir)/config/rules.mk
libs::
@$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) \
@$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$(srcdir)/browserconfig.properties > $(FINAL_TARGET)/browserconfig.properties
install::
@$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) \
@$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$(srcdir)/browserconfig.properties > $(DESTDIR)$(mozappdir)/browserconfig.properties

208
config/Expression.py Normal file
View File

@ -0,0 +1,208 @@
# ***** 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:
rv = Expression.__ASTLeaf('int', int(self.content[:word_len]))
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

@ -165,3 +165,11 @@ ifdef MKDEPEND_DIR
clean clobber realclean clobber_all::
cd $(MKDEPEND_DIR); $(MAKE) $@
endif
PYUNITS := unit-Expression.py unit-Preprocessor.py
check::
@$(EXIT_ON_ERROR) \
for test in $(PYUNITS); do \
$(PYTHON) $(srcdir)/tests/$$test ; \
done

465
config/Preprocessor.py Normal file
View File

@ -0,0 +1,465 @@
"""
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 os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
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)
aLine = aLine.rstrip('\r\n') + self.LE
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:].
"""
includes = []
def handleI(option, opt, value, parser):
includes.append(value)
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('=')
assert len(vals) < 3
if len(vals) == 1:
vals.append(1)
self.context[vals[0]] = vals[1]
def handleU(option, opt, value, parser):
del self.context[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='callback', callback=handleI, type="string",
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('--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 #')
(options, args) = p.parse_args(args=args)
if defaultToStdin and len(args) == 0:
args = [sys.stdin]
includes.extend(args)
for f in includes:
self.do_include(f)
pass
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:
if val[0] == '0':
val = int(val, 8)
else:
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)
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)
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

@ -777,7 +777,7 @@ endif
# Now test variables that might have been set or overridden by $(MY_CONFIG).
DEFINES += -DOSTYPE=\"$(OS_CONFIG)\"
DEFINES += -DOSARCH=\"$(OS_ARCH)\"
DEFINES += -DOSARCH=$(OS_ARCH)
# For profiling
ifdef ENABLE_EAZEL_PROFILER

View File

@ -1436,7 +1436,7 @@ libs:: $(PREF_JS_EXPORTS)
for i in $(PREF_JS_EXPORTS); do \
dest=$(FINAL_TARGET)/$(PREF_DIR)/`basename $$i`; \
$(RM) -f $$dest; \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
done
endif
@ -1447,7 +1447,7 @@ install:: $(PREF_JS_EXPORTS)
for i in $(PREF_JS_EXPORTS); do \
dest=$(DESTDIR)$(mozappdir)/$(PREF_DIR)/`basename $$i`; \
$(RM) -f $$dest; \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
done
endif
endif
@ -1676,7 +1676,7 @@ ifndef NO_DIST_INSTALL
for i in $^; do \
dest=$(FINAL_TARGET)/components/`basename $$i`; \
$(RM) -f $$dest; \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
done
endif
@ -1686,7 +1686,7 @@ ifndef NO_INSTALL
for i in $^; do \
dest=$(DESTDIR)$(mozappdir)/components/`basename $$i`; \
$(RM) -f $$dest; \
$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
done
endif
endif
@ -1732,7 +1732,7 @@ ifndef NO_DIST_INSTALL
if test -f $(JAR_MANIFEST); then \
if test ! -d $(FINAL_TARGET)/chrome; then $(NSINSTALL) -D $(FINAL_TARGET)/chrome; fi; \
if test ! -d $(MAKE_JARS_TARGET)/chrome; then $(NSINSTALL) -D $(MAKE_JARS_TARGET)/chrome; fi; \
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
$(JAR_MANIFEST) | \
$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/make-jars.pl \
-d $(MAKE_JARS_TARGET)/chrome -j $(FINAL_TARGET)/chrome \
@ -1749,7 +1749,7 @@ ifndef NO_INSTALL
if test -f $(JAR_MANIFEST); then \
if test ! -d $(DESTDIR)$(mozappdir)/chrome; then $(NSINSTALL) -D $(DESTDIR)$(mozappdir)/chrome; fi; \
if test ! -d $(MAKE_JARS_TARGET)/chrome; then $(NSINSTALL) -D $(MAKE_JARS_TARGET)/chrome; fi; \
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
$(JAR_MANIFEST) | \
$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/make-jars.pl \
-d $(MAKE_JARS_TARGET)/chrome -j $(DESTDIR)$(mozappdir)/chrome \
@ -1763,7 +1763,7 @@ libs:: $(DIST_FILES)
for f in $(DIST_FILES); do \
dest=$(FINAL_TARGET)/`basename $$f`; \
$(RM) -f $$dest; \
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
$(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(srcdir)/$$f > $$dest; \
done
@ -1775,7 +1775,7 @@ libs:: $(DIST_CHROME_FILES)
for f in $(DIST_CHROME_FILES); do \
dest=$(FINAL_TARGET)/chrome/`basename $$f`; \
$(RM) -f $$dest; \
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
$(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
$(srcdir)/$$f > $$dest; \
done

View File

@ -0,0 +1,63 @@
import unittest
import sys
import os.path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from Expression import Expression, Context
class TestContext(unittest.TestCase):
"""
Unit tests for the Context class
"""
def setUp(self):
self.c = Context()
self.c['FAIL'] = 'PASS'
def test_string_literal(self):
"""test string literal, fall-through for undefined var in a Context"""
self.assertEqual(self.c['PASS'], 'PASS')
def test_variable(self):
"""test value for defined var in the Context class"""
self.assertEqual(self.c['FAIL'], 'PASS')
def test_in(self):
"""test 'var in context' to not fall for fallback"""
self.assert_('FAIL' in self.c)
self.assert_('PASS' not in self.c)
class TestExpression(unittest.TestCase):
"""
Unit tests for the Expression class
evaluate() is called with a context {FAIL: 'PASS'}
"""
def setUp(self):
self.c = Context()
self.c['FAIL'] = 'PASS'
def test_string_literal(self):
"""Test for a string literal in an Expression"""
self.assertEqual(Expression('PASS').evaluate(self.c), 'PASS')
def test_variable(self):
"""Test for variable value in an Expression"""
self.assertEqual(Expression('FAIL').evaluate(self.c), 'PASS')
def test_not(self):
"""Test for the ! operator"""
self.assert_(Expression('!0').evaluate(self.c))
self.assert_(not Expression('!1').evaluate(self.c))
def test_equals(self):
""" Test for the == operator"""
self.assert_(Expression('FAIL == PASS').evaluate(self.c))
def test_notequals(self):
""" Test for the != operator"""
self.assert_(Expression('FAIL != 1').evaluate(self.c))
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,376 @@
import unittest
from StringIO import StringIO
import os
import sys
import os.path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from Preprocessor import Preprocessor
class NamedIO(StringIO):
def __init__(self, name, content):
self.name = name
StringIO.__init__(self, content)
class TestPreprocessor(unittest.TestCase):
"""
Unit tests for the Context class
"""
def setUp(self):
self.pp = Preprocessor()
self.pp.out = StringIO()
def test_conditional_if_0(self):
f = NamedIO("conditional_if_0.in", """#if 0
FAIL
#else
PASS
#endif
""")
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_string_value(self):
f = NamedIO("string_value.in", """#define FOO STRING
#if FOO
string value is true
#else
string value is false
#endif
""")
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "string value is false\n")
def test_number_value(self):
f = NamedIO("string_value.in", """#define FOO 1
#if FOO
number value is true
#else
number value is false
#endif
""")
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "number value is true\n")
def test_conditional_if_0_elif_1(self):
f = NamedIO('conditional_if_0_elif_1.in', '''#if 0
#elif 1
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_if_1(self):
f = NamedIO('conditional_if_1.in', '''#if 1
PASS
#else
FAILE
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_if_1_elif_1_else(self):
f = NamedIO('conditional_if_1_elif_1_else.in', '''#if 1
PASS
#elif 1
FAIL
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_if_1_if_1(self):
f = NamedIO('conditional_if_1_if_1.in', '''#if 1
#if 1
PASS
#else
FAIL
#endif
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_not_0(self):
f = NamedIO('conditional_not_0.in', '''#if !0
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_not_1(self):
f = NamedIO('conditional_not_1.in', '''#if !1
FAIL
#else
PASS
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_conditional_not_emptyval(self):
f = NamedIO('conditional_not_emptyval.in', '''#define EMPTYVAL
#if !EMPTYVAL
FAIL
#else
PASS
#endif
#if EMPTYVAL
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\nPASS\n")
def test_conditional_not_nullval(self):
f = NamedIO('conditional_not_nullval.in', '''#define NULLVAL 0
#if !NULLVAL
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_expand(self):
f = NamedIO('expand.in', '''#define ASVAR AS
#expand P__ASVAR__S
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_undef_defined(self):
f = NamedIO('undef_defined.in', '''#define BAR
#undef BAR
BAR
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "BAR\n")
def test_undef_undefined(self):
f = NamedIO('undef_undefined.in', '''#undef VAR
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "")
def test_filter_attemptSubstitution(self):
f = NamedIO('filter_attemptSubstitution.in', '''#filter attemptSubstitution
P@VAR@ASS
#unfilter attemptSubstitution
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_filter_slashslash(self):
f = NamedIO('filter_slashslash.in', '''#filter slashslash
PASS//FAIL // FAIL
#unfilter slashslash
PASS // PASS
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\nPASS // PASS\n")
def test_filter_spaces(self):
f = NamedIO('filter_spaces.in', '''#filter spaces
You should see two nice ascii tables
+-+-+-+
| | | |
+-+-+-+
#unfilter spaces
+-+---+
| | |
+-+---+
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), """You should see two nice ascii tables
+-+-+-+
| | | |
+-+-+-+
+-+---+
| | |
+-+---+
""")
def test_filter_substitution(self):
f = NamedIO('filter_substitution.in', '''#define VAR ASS
#filter substitution
P@VAR@
#unfilter substitution
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_error(self):
f = NamedIO('error.in', '''#error spit this message out
''')
caught_msg = None
try:
self.pp.do_include(f)
except Preprocessor.Error, e:
caught_msg = e.args[0][-1]
self.assertEqual(caught_msg, 'spit this message out')
def test_javascript_line(self):
f = NamedIO('javascript_line.js.in', '''// Line 1
#if 0
// line 3
#endif
// line 5
# comment
// line 7
// line 8
// line 9
# another comment
// line 11
#define LINE 1
// line 13, given line number overwritten with 2
''')
self.pp.do_include(f)
out = """// Line 1
//@line 5 "CWDjavascript_line.js.in"
// line 5
//@line 7 "CWDjavascript_line.js.in"
// line 7
// line 8
// line 9
//@line 11 "CWDjavascript_line.js.in"
// line 11
//@line 2 "CWDjavascript_line.js.in"
// line 13, given line number overwritten with 2
"""
out = out.replace('CWD', os.getcwd() + os.path.sep)
self.assertEqual(self.pp.out.getvalue(), out)
def test_literal(self):
f = NamedIO('literal.in', '''#literal PASS
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_directory(self):
f = NamedIO('var_directory.in', '''#ifdef DIRECTORY
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_file(self):
f = NamedIO('var_file.in', '''#ifdef FILE
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_if_0(self):
f = NamedIO('var_if_0.in', '''#define VAR 0
#if VAR
FAIL
#else
PASS
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_if_0_elifdef(self):
f = NamedIO('var_if_0_elifdef.in', '''#if 0
#elifdef FILE
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_if_0_elifndef(self):
f = NamedIO('var_if_0_elifndef.in', '''#if 0
#elifndef VAR
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_ifdef_0(self):
f = NamedIO('var_ifdef_0.in', '''#define VAR 0
#ifdef VAR
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_ifdef_undef(self):
f = NamedIO('var_ifdef_undef.in', '''#define VAR 0
#undef VAR
#ifdef VAR
FAIL
#else
PASS
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_ifndef_0(self):
f = NamedIO('var_ifndef_0.in', '''#define VAR 0
#ifndef VAR
FAIL
#else
PASS
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_ifndef_undef(self):
f = NamedIO('var_ifndef_undef.in', '''#define VAR 0
#undef VAR
#ifndef VAR
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
def test_var_line(self):
f = NamedIO('var_line.in', '''#ifdef LINE
PASS
#else
FAIL
#endif
''')
self.pp.do_include(f)
self.assertEqual(self.pp.out.getvalue(), "PASS\n")
if __name__ == '__main__':
unittest.main()

View File

@ -241,7 +241,7 @@ nsPlatformCharset::InitGetCharset(nsACString &oString)
nsCAutoString propertyFile;
// note: NS_LITERAL_CSTRING("unixcharset." OSARCH ".properties") does not compile on AIX
propertyFile.AssignLiteral("unixcharset.");
propertyFile.Append(OSARCH);
propertyFile.AppendLiteral(NS_STRINGIFY(OSARCH));
propertyFile.AppendLiteral(".properties");
nsGREResProperties *info = new nsGREResProperties(propertyFile);
NS_ASSERTION(info, "cannot create nsGREResProperties");

View File

@ -182,7 +182,7 @@ _FILES = \
GARBAGE += $(addprefix $(DIST)/bin/res/,$(_FILES))
FORMS_CSS_SRC = $(srcdir)/forms.css
PREPROCESS_FORMS_CSS = $(PERL) $(MOZILLA_DIR)/config/preprocessor.pl --marker=% $(DEFINES) $(ACDEFINES) $(FORMS_CSS_SRC)
PREPROCESS_FORMS_CSS = $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py --marker=% $(DEFINES) $(ACDEFINES) $(FORMS_CSS_SRC)
$(DIST)/bin/res/forms.css $(DESTDIR)$(mozappdir)/res/forms.css: $(FORMS_CSS_SRC) Makefile
if test ! -d $(@D); then rm -rf $(@D); $(NSINSTALL) -D $(@D); else true; fi

View File

@ -564,7 +564,7 @@ input[type="file"] > input[type="text"] {
input[type="file"] { height: 2em; }
}
%if OSARCH=="OS2"
%if OSARCH==OS2
input {
font: medium serif; font-family: inherit
}

View File

@ -47,9 +47,9 @@ DEFINES += -DAB_CD=$(AB_CD) -DMOZ_DISTRIBUTION_ID_UNQUOTED=$(MOZ_DISTRIBUTION_ID
include $(topsrcdir)/config/rules.mk
libs::
@$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) \
@$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$(srcdir)/browserconfig.properties > $(FINAL_TARGET)/browserconfig.properties
install::
@$(PERL) $(topsrcdir)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) \
@$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$(srcdir)/browserconfig.properties > $(DESTDIR)$(mozappdir)/browserconfig.properties

View File

@ -81,7 +81,7 @@ TEST_DRIVER_PPARGS = -DBROWSER_PATH=$(browser_path) \
$(NULL)
runtests.pl: runtests.pl.in
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@

View File

@ -50,5 +50,5 @@ GARBAGE += nsHelperAppDlg.js
include $(topsrcdir)/config/rules.mk
nsHelperAppDlg.js: nsHelperAppDlg.js.in
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $^ > $@
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@

View File

@ -50,5 +50,5 @@ GARBAGE += nsExtensionManager.js
include $(topsrcdir)/config/rules.mk
nsExtensionManager.js: nsExtensionManager.js.in
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $^ > $@
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@

View File

@ -246,7 +246,7 @@ ifdef MOZ_PKG_REMOVALS
MOZ_PKG_REMOVALS_GEN = removed-files
$(MOZ_PKG_REMOVALS_GEN): $(MOZ_PKG_REMOVALS) Makefile Makefile.in
$(PERL) $(topsrcdir)/config/preprocessor.pl -Fsubstitution $(DEFINES) $(ACDEFINES) $(MOZ_PKG_REMOVALS) > $(MOZ_PKG_REMOVALS_GEN)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) $(MOZ_PKG_REMOVALS) > $(MOZ_PKG_REMOVALS_GEN)
endif
GARBAGE += $(DIST)/$(PACKAGE) $(PACKAGE)

View File

@ -58,7 +58,7 @@ endif
include $(topsrcdir)/config/rules.mk
nsUpdateService.js: nsUpdateService.js.in
$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl $(DEFINES) $(ACDEFINES) $^ > $@
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@
ifdef ENABLE_TESTS
check:: nsUpdateService.js