bug 663180 - write $objdir/mozinfo.json during configure. r=khuey,jhammel
authorTed Mielczarek <ted.mielczarek@gmail.com>
Fri, 10 Jun 2011 12:44:33 -0400
changeset 70965 6d19baaa339feb3a2085a116d958fba691cbf8f9
parent 70964 a9e0ace0ec38a17760f63d0b2c516b2b3ce0053f
child 70966 d12dda590f4a72e59bf80352ed6a53d8fdb6015c
push id46
push usereakhgari@mozilla.com
push dateMon, 13 Jun 2011 19:03:46 +0000
treeherdermozilla-inbound@17219648b629 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, jhammel
bugs663180
milestone7.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 663180 - write $objdir/mozinfo.json during configure. r=khuey,jhammel
config/Makefile.in
config/tests/unit-writemozinfo.py
config/writemozinfo.py
configure.in
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -184,16 +184,17 @@ endif
 PYUNITS := \
   unit-Expression.py \
   unit-Preprocessor.py \
   unit-nsinstall.py \
   unit-printprereleasesuffix.py \
   unit-JarMaker.py \
   unit-buildlist.py \
   unit-expandlibs.py \
+  unit-writemozinfo.py \
   $(NULL)
 
 check:: check-python-modules check-jar-mn
 
 check-python-modules::
 	@$(EXIT_ON_ERROR) \
 	for test in $(PYUNITS); do \
 	  $(PYTHON) $(srcdir)/tests/$$test ; \
new file mode 100755
--- /dev/null
+++ b/config/tests/unit-writemozinfo.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python
+from __future__ import with_statement
+import unittest
+import os, sys, time, tempfile
+from StringIO import StringIO
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from writemozinfo import build_dict, write_json, JsonValue, jsonify
+
+class TestBuildDict(unittest.TestCase):
+    def testMissing(self):
+        """
+        Test that missing required values raises.
+        """
+        self.assertRaises(Exception, build_dict, {})
+        self.assertRaises(Exception, build_dict, {'OS_TARGET':'foo'})
+        self.assertRaises(Exception, build_dict, {'TARGET_CPU':'foo'})
+
+    def testWin(self):
+        d = build_dict({'OS_TARGET':'WINNT',
+                        'TARGET_CPU':'i386'})
+        self.assertEqual('win', d['os'])
+        self.assertEqual('x86', d['processor'])
+        self.assertEqual(32, d['bits'])
+
+    def testLinux(self):
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'i386'})
+        self.assertEqual('linux', d['os'])
+        self.assertEqual('x86', d['processor'])
+        self.assertEqual(32, d['bits'])
+
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'x86_64'})
+        self.assertEqual('linux', d['os'])
+        self.assertEqual('x86_64', d['processor'])
+        self.assertEqual(64, d['bits'])
+
+    def testMac(self):
+        d = build_dict({'OS_TARGET':'Darwin',
+                        'TARGET_CPU':'i386'})
+        self.assertEqual('mac', d['os'])
+        self.assertEqual('x86', d['processor'])
+        self.assertEqual(32, d['bits'])
+
+        d = build_dict({'OS_TARGET':'Darwin',
+                        'TARGET_CPU':'x86_64'})
+        self.assertEqual('mac', d['os'])
+        self.assertEqual('x86_64', d['processor'])
+        self.assertEqual(64, d['bits'])
+
+    def testAndroid(self):
+        d = build_dict({'OS_TARGET':'Android',
+                        'TARGET_CPU':'arm'})
+        self.assertEqual('android', d['os'])
+        self.assertEqual('arm', d['processor'])
+        self.assertEqual(32, d['bits'])
+
+    def testX86(self):
+        """
+        Test that various i?86 values => x86.
+        """
+        d = build_dict({'OS_TARGET':'WINNT',
+                        'TARGET_CPU':'i486'})
+        self.assertEqual('x86', d['processor'])
+
+        d = build_dict({'OS_TARGET':'WINNT',
+                        'TARGET_CPU':'i686'})
+        self.assertEqual('x86', d['processor'])
+
+    def testARM(self):
+        """
+        Test that all arm CPU architectures => arm.
+        """
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'arm'})
+        self.assertEqual('arm', d['processor'])
+
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'armv7'})
+        self.assertEqual('arm', d['processor'])
+
+    def testUnknown(self):
+        """
+        Test that unknown values pass through okay.
+        """
+        d = build_dict({'OS_TARGET':'RandOS',
+                        'TARGET_CPU':'cptwo'})
+        self.assertEqual("randos", d["os"])
+        self.assertEqual("cptwo", d["processor"])
+        # unknown CPUs should not get a bits value
+        self.assertFalse("bits" in d)
+        
+    def testDebug(self):
+        """
+        Test that debug values are properly detected.
+        """
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'i386'})
+        self.assertEqual(False, d['debug'])
+        
+        d = build_dict({'OS_TARGET':'Linux',
+                        'TARGET_CPU':'i386',
+                        'MOZ_DEBUG':'1'})
+        self.assertEqual(True, d['debug'])
+
+class TestJsonValue(unittest.TestCase):
+    def testNone(self):
+        self.assertEqual("null", repr(JsonValue(None)))
+        
+    def testBool(self):
+        self.assertEqual("true", repr(JsonValue(True)))
+        self.assertEqual("false", repr(JsonValue(False)))
+
+    def testStr(self):
+        self.assertEqual("'abc'", repr(JsonValue("abc")))
+
+    def testInt(self):
+        self.assertEqual("100", repr(JsonValue(100)))
+
+    def testInvalid(self):
+        self.assertRaises(Exception, JsonValue, unicode("abc"))
+        self.assertRaises(Exception, JsonValue, 123.45)
+
+def parse_json(j):
+    """
+    Awful hack to parse a restricted subset of JSON strings into Python dicts.
+    """
+    return eval(j, {'true':True,'false':False,'null':None})
+
+class TestJsonify(unittest.TestCase):
+    """
+    Test the jsonify function.
+    """
+    def testBasic(self):
+        """
+        Sanity check the set of accepted Python value types.
+        """
+        j = parse_json(jsonify({'a':True,'b':False,'c':None,'d':100,'e':"abc"}))
+        self.assertEquals(True, j['a'])
+        self.assertEquals(False, j['b'])
+        self.assertEquals(None, j['c'])
+        self.assertEquals(100, j['d'])
+        self.assertEquals("abc", j['e'])
+
+class TestWriteJson(unittest.TestCase):
+    """
+    Test the write_json function.
+    """
+    def setUp(self):
+        fd, self.f = tempfile.mkstemp()
+        os.close(fd)
+
+    def tearDown(self):
+        os.unlink(self.f)
+
+    def testBasic(self):
+        """
+        Test that writing to a file produces correct output.
+        """
+        write_json(self.f, env={'OS_TARGET':'WINNT',
+                                'TARGET_CPU':'i386'})
+        with open(self.f) as f:
+            d = parse_json(f.read())
+            self.assertEqual('win', d['os'])
+            self.assertEqual('x86', d['processor'])
+            self.assertEqual(32, d['bits'])
+
+    def testFileObj(self):
+        """
+        Test that writing to a file-like object produces correct output.
+        """
+        s = StringIO()
+        write_json(s, env={'OS_TARGET':'WINNT',
+                           'TARGET_CPU':'i386'})
+        d = parse_json(s.getvalue())
+        self.assertEqual('win', d['os'])
+        self.assertEqual('x86', d['processor'])
+        self.assertEqual(32, d['bits'])
+
+if __name__ == '__main__':
+    unittest.main()
+  
new file mode 100755
--- /dev/null
+++ b/config/writemozinfo.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+#
+# This script is run during configure, taking variables set in configure
+# and producing a JSON file that describes some portions of the build
+# configuration, such as the target OS and CPU.
+#
+# The output file is intended to be used as input to the mozinfo package.
+from __future__ import with_statement
+import os, re, sys
+
+def build_dict(env=os.environ):
+    """
+    Build a dict containing data about the build configuration from
+    the environment.
+    """
+    d = {}
+    # Check that all required variables are present first.
+    required = ["TARGET_CPU", "OS_TARGET"]
+    missing = [r for r in required if r not in env]
+    if missing:
+        raise Exception("Missing required environment variables: " %
+                        ', '.join(missing))
+    # os
+    o = env["OS_TARGET"]
+    known_os = {"Linux": "linux",
+                "WINNT": "win",
+                "Darwin": "mac",
+                "Android": "android"}
+    if o in known_os:
+        d["os"] = known_os[o]
+    else:
+        # Allow unknown values, just lowercase them.
+        d["os"] = o.lower()
+    
+    # processor
+    p = env["TARGET_CPU"]
+    # do some slight massaging for some values
+    #TODO: retain specific values in case someone wants them?
+    if p.startswith("arm"):
+        p = "arm"
+    elif re.match("i[3-9]86", p):
+        p = "x86"
+    d["processor"] = p
+    # hardcoded list of 64-bit CPUs
+    if p in ["x86_64", "ppc64"]:
+        d["bits"] = 64
+    # hardcoded list of known 32-bit CPUs
+    elif p in ["x86", "arm", "ppc"]:
+        d["bits"] = 32
+    # other CPUs will wind up with unknown bits
+
+    # debug
+    d["debug"] = 'MOZ_DEBUG' in env and env['MOZ_DEBUG'] == '1'
+    return d
+
+#TODO: replace this with the json module when Python >= 2.6 is a requirement.
+class JsonValue:
+    """
+    A class to serialize Python values into JSON-compatible representations.
+    """
+    def __init__(self, v):
+        if v is not None and not (isinstance(v,str) or isinstance(v,bool) or isinstance(v,int)):
+            raise Exception("Unhandled data type: %s" % type(v))
+        self.v = v
+    def __repr__(self):
+        if self.v is None:
+            return "null"
+        if isinstance(self.v,bool):
+            return str(self.v).lower()
+        return repr(self.v)
+
+def jsonify(d):
+    """
+    Return a JSON string of the dict |d|. Only handles a subset of Python
+    value types: bool, str, int, None.
+    """
+    jd = {}
+    for k, v in d.iteritems():
+        jd[k] = JsonValue(v)
+    return repr(jd)
+
+def write_json(file, env=os.environ):
+    """
+    Write JSON data about the configuration specified in |env|
+    to |file|, which may be a filename or file-like object.
+    See build_dict for information about what  environment variables are used,
+    and what keys are produced.
+    """
+    s = jsonify(build_dict(env))
+    if isinstance(file, basestring):
+        with open(file, "w") as f:
+            f.write(s)
+    else:
+        file.write(s)
+
+if __name__ == '__main__':
+    try:
+        write_json(sys.argv[1] if len(sys.argv) > 1 else sys.stdout)
+    except Exception, e:
+        print >>sys.stderr, str(e)
+        sys.exit(1)
--- a/configure.in
+++ b/configure.in
@@ -9319,16 +9319,26 @@ echo $MAKEFILES | ${PERL} $srcdir/build/
 rm conftest.sh
 
 echo $MAKEFILES > unallmakefiles
 
 mv -f config/autoconf.mk config/autoconf.mk.orig 2> /dev/null
 
 AC_OUTPUT($MAKEFILES)
 
+# Generate a JSON config file for unittest harnesses etc to read
+# build configuration details from in a standardized way.
+OS_TARGET=${OS_TARGET} TARGET_CPU=${TARGET_CPU} MOZ_DEBUG=${MOZ_DEBUG} \
+  $PYTHON ${_topsrcdir}/config/writemozinfo.py ./mozinfo.json.tmp
+if cmp -s ./mozinfo.json.tmp ./mozinfo.json; then
+  rm ./mozinfo.json.tmp
+else
+  mv -f ./mozinfo.json.tmp ./mozinfo.json
+fi
+
 dnl Prevent the regeneration of cairo-features.h forcing rebuilds of gfx stuff
 if test "$CAIRO_FEATURES_H"; then
   if cmp -s $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig; then
     echo "$CAIRO_FEATURES_H is unchanged"
     mv -f "$CAIRO_FEATURES_H".orig "$CAIRO_FEATURES_H" 2> /dev/null
   else
     rm -f "$CAIRO_FEATURES_H".orig 2> /dev/null
   fi