summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/backend/make.py
blob: 90b37e675847efe51a5f69da31397474097c1fde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import mozpack.path as mozpath

from mozbuild.frontend.data import GeneratedFile
from mozbuild.shellutil import quote as shell_quote

from .common import CommonBackend


class MakeBackend(CommonBackend):
    """Class encapsulating logic for backends that use Make."""

    def _init(self):
        CommonBackend._init(self)

    def _format_statements_for_generated_file(self, obj, tier, extra_dependencies=""):
        """Return the list of statements to write to the Makefile for this
        GeneratedFile.

        This function will invoke _format_generated_file_input_name and
        _format_generated_file_output_name to munge the input/output filenames
        before sending them to the output.
        """
        assert isinstance(obj, GeneratedFile)

        # Localized generated files can use {AB_CD} and {AB_rCD} in their
        # output paths.
        if obj.localized:
            substs = {"AB_CD": "$(AB_CD)", "AB_rCD": "$(AB_rCD)"}
        else:
            substs = {}

        outputs = []
        needs_AB_rCD = False
        for o in obj.outputs:
            needs_AB_rCD = needs_AB_rCD or ("AB_rCD" in o)
            try:
                outputs.append(
                    self._format_generated_file_output_name(o.format(**substs), obj)
                )
            except KeyError as e:
                raise ValueError(
                    "%s not in %s is not a valid substitution in %s"
                    % (e.args[0], ", ".join(sorted(substs.keys())), o)
                )

        first_output = outputs[0]
        dep_file = mozpath.join(
            mozpath.dirname(first_output),
            "$(MDDEPDIR)",
            "%s.pp" % mozpath.basename(first_output),
        )
        # The stub target file needs to go in MDDEPDIR so that it doesn't
        # get written into generated Android resource directories, breaking
        # Gradle tooling and/or polluting the Android packages.
        stub_file = mozpath.join(
            mozpath.dirname(first_output),
            "$(MDDEPDIR)",
            "%s.stub" % mozpath.basename(first_output),
        )

        if obj.inputs:
            inputs = [
                self._format_generated_file_input_name(f, obj) for f in obj.inputs
            ]
        else:
            inputs = []

        force = ""
        if obj.force:
            force = " FORCE"
        elif obj.localized:
            force = " $(if $(IS_LANGUAGE_REPACK),FORCE)"

        ret = []

        if obj.script:
            # If we are doing an artifact build, we don't run compiler, so
            # we can skip generated files that are needed during compile,
            # or let the rule run as the result of something depending on
            # it.
            if (
                not (obj.required_before_compile or obj.required_during_compile)
                or not self.environment.is_artifact_build
            ):
                if tier and not needs_AB_rCD:
                    # Android localized resources have special Makefile
                    # handling.

                    # Double-colon tiers via a variable that the backend adds as a dependency
                    # later. See https://bugzilla.mozilla.org/show_bug.cgi?id=1645986#c0 as
                    # to why.
                    if tier in ("export", "pre-compile", "libs", "misc"):
                        dep = "%s_TARGETS" % tier.replace("-", "_").upper()
                        ret.append("%s += %s" % (dep, stub_file))
                    else:
                        ret.append("%s: %s" % (tier, stub_file))
            for output in outputs:
                ret.append("%s: %s ;" % (output, stub_file))
            ret.append("EXTRA_MDDEPEND_FILES += %s" % dep_file)

            ret.append(
                (
                    """{stub}: {script}{inputs}{backend}{force}
\t$(REPORT_BUILD)
\t$(call py_action,file_generate,{locale}{script} """  # wrap for E501
                    """{method} {output} {dep_file} {stub}{inputs}{flags})
\t@$(TOUCH) $@
"""
                ).format(
                    stub=stub_file,
                    output=first_output,
                    dep_file=dep_file,
                    inputs=" " + " ".join(inputs) if inputs else "",
                    flags=" " + " ".join(shell_quote(f) for f in obj.flags)
                    if obj.flags
                    else "",
                    backend=" " + extra_dependencies if extra_dependencies else "",
                    # Locale repacks repack multiple locales from a single configured objdir,
                    # so standard mtime dependencies won't work properly when the build is re-run
                    # with a different locale as input. IS_LANGUAGE_REPACK will reliably be set
                    # in this situation, so simply force the generation to run in that case.
                    force=force,
                    locale="--locale=$(AB_CD) " if obj.localized else "",
                    script=obj.script,
                    method=obj.method,
                )
            )

        return ret

    def _format_generated_file_input_name(self, path, obj):
        raise NotImplementedError("Subclass must implement")

    def _format_generated_file_output_name(self, path, obj):
        raise NotImplementedError("Subclass must implement")