1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5# This module produces a JSON file that provides basic build info and
6# configuration metadata.
7
8from __future__ import absolute_import, print_function, unicode_literals
9
10import json
11import os
12import re
13import six
14
15
16def build_dict(config, env=os.environ):
17    """
18    Build a dict containing data about the build configuration from
19    the environment.
20    """
21    substs = config.substs
22
23    # Check that all required variables are present first.
24    required = ["TARGET_CPU", "OS_TARGET"]
25    missing = [r for r in required if r not in substs]
26    if missing:
27        raise Exception(
28            "Missing required environment variables: %s" % ", ".join(missing)
29        )
30
31    d = {}
32    d["topsrcdir"] = config.topsrcdir
33
34    if config.mozconfig:
35        d["mozconfig"] = config.mozconfig
36
37    # os
38    o = substs["OS_TARGET"]
39    known_os = {"Linux": "linux", "WINNT": "win", "Darwin": "mac", "Android": "android"}
40    if o in known_os:
41        d["os"] = known_os[o]
42    else:
43        # Allow unknown values, just lowercase them.
44        d["os"] = o.lower()
45
46    # Widget toolkit, just pass the value directly through.
47    d["toolkit"] = substs.get("MOZ_WIDGET_TOOLKIT")
48
49    # Application name
50    if "MOZ_APP_NAME" in substs:
51        d["appname"] = substs["MOZ_APP_NAME"]
52
53    # Build app name
54    if "MOZ_BUILD_APP" in substs:
55        d["buildapp"] = substs["MOZ_BUILD_APP"]
56
57    # processor
58    p = substs["TARGET_CPU"]
59    # do some slight massaging for some values
60    # TODO: retain specific values in case someone wants them?
61    if p.startswith("arm"):
62        p = "arm"
63    elif re.match("i[3-9]86", p):
64        p = "x86"
65    d["processor"] = p
66    # hardcoded list of 64-bit CPUs
67    if p in ["x86_64", "ppc64", "aarch64"]:
68        d["bits"] = 64
69    # hardcoded list of known 32-bit CPUs
70    elif p in ["x86", "arm", "ppc"]:
71        d["bits"] = 32
72    # other CPUs will wind up with unknown bits
73
74    d["debug"] = substs.get("MOZ_DEBUG") == "1"
75    d["nightly_build"] = substs.get("NIGHTLY_BUILD") == "1"
76    d["early_beta_or_earlier"] = substs.get("EARLY_BETA_OR_EARLIER") == "1"
77    d["release_or_beta"] = substs.get("RELEASE_OR_BETA") == "1"
78    d["devedition"] = substs.get("MOZ_DEV_EDITION") == "1"
79    d["pgo"] = substs.get("MOZ_PGO") == "1"
80    d["crashreporter"] = bool(substs.get("MOZ_CRASHREPORTER"))
81    d["normandy"] = substs.get("MOZ_NORMANDY") == "1"
82    d["datareporting"] = bool(substs.get("MOZ_DATA_REPORTING"))
83    d["healthreport"] = substs.get("MOZ_SERVICES_HEALTHREPORT") == "1"
84    d["sync"] = substs.get("MOZ_SERVICES_SYNC") == "1"
85    # FIXME(emilio): We need to update a lot of WPT expectations before removing this.
86    d["stylo"] = True
87    d["asan"] = substs.get("MOZ_ASAN") == "1"
88    d["tsan"] = substs.get("MOZ_TSAN") == "1"
89    d["ubsan"] = substs.get("MOZ_UBSAN") == "1"
90    d["telemetry"] = substs.get("MOZ_TELEMETRY_REPORTING") == "1"
91    d["tests_enabled"] = substs.get("ENABLE_TESTS") == "1"
92    d["bin_suffix"] = substs.get("BIN_SUFFIX", "")
93    d["require_signing"] = substs.get("MOZ_REQUIRE_SIGNING") == "1"
94    d["official"] = bool(substs.get("MOZILLA_OFFICIAL"))
95    d["updater"] = substs.get("MOZ_UPDATER") == "1"
96    d["artifact"] = substs.get("MOZ_ARTIFACT_BUILDS") == "1"
97    d["ccov"] = substs.get("MOZ_CODE_COVERAGE") == "1"
98    d["cc_type"] = substs.get("CC_TYPE")
99    d["non_native_theme"] = True
100    d["domstreams"] = substs.get("MOZ_DOM_STREAMS") == "1"
101
102    def guess_platform():
103        if d["buildapp"] == "browser":
104            p = d["os"]
105            if p == "mac":
106                p = "macosx64"
107            elif d["bits"] == 64:
108                p = "{}64".format(p)
109            elif p in ("win",):
110                p = "{}32".format(p)
111
112            if d["asan"]:
113                p = "{}-asan".format(p)
114
115            return p
116
117        if d["buildapp"] == "mobile/android":
118            if d["processor"] == "x86":
119                return "android-x86"
120            if d["processor"] == "x86_64":
121                return "android-x86_64"
122            if d["processor"] == "aarch64":
123                return "android-aarch64"
124            return "android-arm"
125
126    def guess_buildtype():
127        if d["debug"]:
128            return "debug"
129        if d["pgo"]:
130            return "pgo"
131        return "opt"
132
133    # if buildapp or bits are unknown, we don't have a configuration similar to
134    # any in automation and the guesses are useless.
135    if "buildapp" in d and (d["os"] == "mac" or "bits" in d):
136        d["platform_guess"] = guess_platform()
137        d["buildtype_guess"] = guess_buildtype()
138
139    if (
140        d.get("buildapp", "") == "mobile/android"
141        and "MOZ_ANDROID_MIN_SDK_VERSION" in substs
142    ):
143        d["android_min_sdk"] = substs["MOZ_ANDROID_MIN_SDK_VERSION"]
144
145    return d
146
147
148def write_mozinfo(file, config, env=os.environ):
149    """Write JSON data about the configuration specified in config and an
150    environment variable dict to ``|file|``, which may be a filename or file-like
151    object.
152    See build_dict for information about what  environment variables are used,
153    and what keys are produced.
154    """
155    build_conf = build_dict(config, env)
156    if isinstance(file, six.text_type):
157        file = open(file, "wt")
158
159    json.dump(build_conf, file, sort_keys=True, indent=4)
160