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
101    def guess_platform():
102        if d["buildapp"] == "browser":
103            p = d["os"]
104            if p == "mac":
105                p = "macosx64"
106            elif d["bits"] == 64:
107                p = "{}64".format(p)
108            elif p in ("win",):
109                p = "{}32".format(p)
110
111            if d["asan"]:
112                p = "{}-asan".format(p)
113
114            return p
115
116        if d["buildapp"] == "mobile/android":
117            if d["processor"] == "x86":
118                return "android-x86"
119            if d["processor"] == "x86_64":
120                return "android-x86_64"
121            if d["processor"] == "aarch64":
122                return "android-aarch64"
123            return "android-arm"
124
125    def guess_buildtype():
126        if d["debug"]:
127            return "debug"
128        if d["pgo"]:
129            return "pgo"
130        return "opt"
131
132    # if buildapp or bits are unknown, we don't have a configuration similar to
133    # any in automation and the guesses are useless.
134    if "buildapp" in d and (d["os"] == "mac" or "bits" in d):
135        d["platform_guess"] = guess_platform()
136        d["buildtype_guess"] = guess_buildtype()
137
138    if (
139        d.get("buildapp", "") == "mobile/android"
140        and "MOZ_ANDROID_MIN_SDK_VERSION" in substs
141    ):
142        d["android_min_sdk"] = substs["MOZ_ANDROID_MIN_SDK_VERSION"]
143
144    return d
145
146
147def write_mozinfo(file, config, env=os.environ):
148    """Write JSON data about the configuration specified in config and an
149    environment variable dict to ``|file|``, which may be a filename or file-like
150    object.
151    See build_dict for information about what  environment variables are used,
152    and what keys are produced.
153    """
154    build_conf = build_dict(config, env)
155    if isinstance(file, six.text_type):
156        file = open(file, "wt")
157
158    json.dump(build_conf, file, sort_keys=True, indent=4)
159