bug 361583, start using Preprocessor.py instead of preprocessor.pl in building Minefield, r=bsmedberg
authoraxel@pike.org
Wed, 11 Apr 2007 09:35:01 -0700
changeset 469 6c4925ef13a7afd095fa402d1569ef4c6822de85
parent 468 789f083cad9e5da0c667d440051accc67b693c3c
child 470 9bd2b06315dcc49489395fbe770dc424e53812c9
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs361583
milestone1.9a4pre
bug 361583, start using Preprocessor.py instead of preprocessor.pl in building Minefield, r=bsmedberg
browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/Makefile.in
browser/branding/unofficial/locales/Makefile.in
config/Expression.py
config/Makefile.in
config/Preprocessor.py
config/config.mk
config/rules.mk
config/tests/unit-Expression.py
config/tests/unit-Preprocessor.py
intl/uconv/src/nsUNIXCharset.cpp
layout/style/Makefile.in
layout/style/forms.css
other-licenses/branding/firefox/locales/Makefile.in
testing/mochitest/Makefile.in
toolkit/mozapps/downloads/src/Makefile.in
toolkit/mozapps/extensions/src/Makefile.in
toolkit/mozapps/installer/packager.mk
toolkit/mozapps/update/src/Makefile.in
--- a/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/Makefile.in
+++ b/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/Makefile.in
@@ -45,15 +45,15 @@ include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DFIREFOX_VERSION=$(FIREFOX_VERSION)
 
 FILES := \
 	install.rdf \
 	$(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::
 	$(SYSINSTALL) $(IFLAGS1) $(FILES) $(DESTDIR)$(mozappdir)/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}
 
 GARBAGE += $(FILES)
--- a/browser/branding/unofficial/locales/Makefile.in
+++ b/browser/branding/unofficial/locales/Makefile.in
@@ -42,14 +42,14 @@ relativesrcdir = browser/branding/unoffi
 
 include $(DEPTH)/config/autoconf.mk
 
 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
new file mode 100644
--- /dev/null
+++ b/config/Expression.py
@@ -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
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -160,8 +160,16 @@ endif
 endif
 
 FORCE:
 
 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
new file mode 100644
--- /dev/null
+++ b/config/Preprocessor.py
@@ -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()
--- a/config/config.mk
+++ b/config/config.mk
@@ -772,17 +772,17 @@ endif
 # Include any personal overrides the user might think are needed.
 #
 -include $(MY_CONFIG)
 
 ######################################################################
 # 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
 ifndef INTERNAL_TOOLS
 ifneq ($(LIBRARY_NAME), xpt)
 ifneq (, $(findstring $(shell $(topsrcdir)/build/unix/print-depth-path.sh | awk -F/ '{ print $$2; }'), $(MOZ_PROFILE_MODULES)))
 PROFILER_CFLAGS	= $(EAZEL_PROFILER_CFLAGS) -DENABLE_EAZEL_PROFILER
 PROFILER_LIBS	= $(EAZEL_PROFILER_LIBS)
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1431,28 +1431,28 @@ endif
 
 ifndef NO_DIST_INSTALL
 libs:: $(PREF_JS_EXPORTS)
 	if test ! -d $(FINAL_TARGET)/$(PREF_DIR); then $(NSINSTALL) -D $(FINAL_TARGET)/$(PREF_DIR); fi
 	$(EXIT_ON_ERROR)  \
 	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
 
 ifndef NO_INSTALL
 install:: $(PREF_JS_EXPORTS)
 	if test ! -d $(DESTDIR)$(mozappdir)/$(PREF_DIR); then $(NSINSTALL) -D $(DESTDIR)$(mozappdir)/$(PREF_DIR); fi
 	$(EXIT_ON_ERROR)  \
 	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
 
 ################################################################################
 # Copy each element of AUTOCFG_JS_EXPORTS to $(FINAL_TARGET)/defaults/autoconfig
 
 ifneq ($(AUTOCFG_JS_EXPORTS),)
@@ -1671,27 +1671,27 @@ endif
 
 ifdef EXTRA_PP_COMPONENTS
 libs:: $(EXTRA_PP_COMPONENTS)
 ifndef NO_DIST_INSTALL
 	$(EXIT_ON_ERROR) \
 	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
 
 install:: $(EXTRA_PP_COMPONENTS)
 ifndef NO_INSTALL
 	$(EXIT_ON_ERROR) \
 	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
 
 ################################################################################
 # SDK
 
 ifneq (,$(SDK_LIBRARY))
@@ -1727,60 +1727,60 @@ chrome::
 	+$(LOOP_OVER_TOOL_DIRS)
 
 libs realchrome:: $(CHROME_DEPS)
 ifndef NO_DIST_INSTALL
 	@$(EXIT_ON_ERROR) \
 	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 \
 	    $(MAKE_JARS_FLAGS) -- "$(XULPPFLAGS) $(DEFINES) $(ACDEFINES)"; \
 	fi
 endif
 
 install:: $(CHROME_DEPS)
 ifneq (,$(filter flat symlink,$(MOZ_CHROME_FILE_FORMAT)))
 	$(error Flat chrome is for debugging only, and should not be used with the install target.)
 endif
 ifndef NO_INSTALL
 	@$(EXIT_ON_ERROR) \
 	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 \
 	    $(MAKE_JARS_FLAGS) -- "$(XULPPFLAGS) $(DEFINES) $(ACDEFINES)"; \
 	fi
 endif
 
 ifneq ($(DIST_FILES),)
 libs:: $(DIST_FILES)
 	@$(EXIT_ON_ERROR) \
 	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
 endif
 
 ifneq ($(DIST_CHROME_FILES),)
 libs:: $(DIST_CHROME_FILES)
 	@$(EXIT_ON_ERROR) \
 	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
 endif
 
 ifneq ($(XPI_PKGNAME),)
 libs realchrome::
 ifdef STRIP_XPI
new file mode 100644
--- /dev/null
+++ b/config/tests/unit-Expression.py
@@ -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()
new file mode 100644
--- /dev/null
+++ b/config/tests/unit-Preprocessor.py
@@ -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()
--- a/intl/uconv/src/nsUNIXCharset.cpp
+++ b/intl/uconv/src/nsUNIXCharset.cpp
@@ -236,17 +236,17 @@ nsPlatformCharset::InitGetCharset(nsACSt
   // locked for thread safety 
   {
     nsAutoLock guard(gLock);
 
     if (!gNLInfo) {
       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");
       if (info) {
         PRBool didLoad = info->DidLoad();
         if (!didLoad) {
           delete info;
           info = nsnull;
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -177,17 +177,17 @@ LOCAL_INCLUDES	= \
 	viewsource.css \
 	arrow.gif \
 	arrowd.gif \
 	$(NULL)
 
 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
 	rm -f $@
 	$(PREPROCESS_FORMS_CSS) > $@
 
 libs:: $(DIST)/bin/res/forms.css
 
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -559,17 +559,17 @@ input[type="file"] > input[type="text"] 
 @media print {
   input, textarea, select, button {
     -moz-user-input: none !important;
   }
 
   input[type="file"] { height: 2em; }
 }
 
-%if OSARCH=="OS2"
+%if OSARCH==OS2
 input {
   font: medium serif; font-family: inherit
 }
 
 select {
   font: medium serif; font-family: inherit
 }
 
--- a/other-licenses/branding/firefox/locales/Makefile.in
+++ b/other-licenses/branding/firefox/locales/Makefile.in
@@ -42,14 +42,14 @@ relativesrcdir = other-licenses/branding
 
 include $(DEPTH)/config/autoconf.mk
 
 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
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -76,14 +76,14 @@ browser_path = \"../$(DIST)/bin/firefox\
 endif
 
 # These go in _tests/ so they need to go up an extra path segement
 TEST_DRIVER_PPARGS = 	-DBROWSER_PATH=$(browser_path) \
 			-DXPC_BIN_PATH=\"../$(DIST)/bin\" \
 			$(NULL)
 
 runtests.pl: runtests.pl.in
-	$(PERL) $(MOZILLA_DIR)/config/preprocessor.pl \
+	$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
 	$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
 
 
 libs:: $(_SERV_FILES)
 	$(INSTALL) $^ $(_DEST_DIR)
--- a/toolkit/mozapps/downloads/src/Makefile.in
+++ b/toolkit/mozapps/downloads/src/Makefile.in
@@ -45,10 +45,10 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = helperAppDlg
 
 EXTRA_COMPONENTS = nsHelperAppDlg.js
 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) $^ > $@ 
 
--- a/toolkit/mozapps/extensions/src/Makefile.in
+++ b/toolkit/mozapps/extensions/src/Makefile.in
@@ -45,10 +45,10 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = extensions
 
 EXTRA_COMPONENTS = nsExtensionManager.js
 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) $^ > $@ 
 
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -241,17 +241,17 @@ libs:: make-package
 endif
 
 DEFINES += -DDLL_PREFIX=$(DLL_PREFIX) -DDLL_SUFFIX=$(DLL_SUFFIX)
 
 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)
 
 ifeq ($(OS_ARCH),IRIX)
 STRIP_FLAGS	= -f
 endif
 ifeq ($(OS_ARCH),BeOS)
--- a/toolkit/mozapps/update/src/Makefile.in
+++ b/toolkit/mozapps/update/src/Makefile.in
@@ -53,14 +53,14 @@ GARBAGE += nsUpdateService.js
 
 ifeq ($(OS_ARCH),WINNT)
 EXTRA_COMPONENTS += nsPostUpdateWin.js
 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
 	$(CYGWIN_WRAPPER) $(RUN_TEST_PROGRAM) $(DIST)/bin/xpcshell$(BIN_SUFFIX) -f nsUpdateService.js $(srcdir)/testnsUpdateService.js
 endif