summaryrefslogtreecommitdiffstats
path: root/python/mozboot/mozboot/mozconfig.py
blob: a1ae4c8523cdbad53b3df3215f3b3cec5a496473 (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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# 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 filecmp
import os
from pathlib import Path
from typing import Union

MOZ_MYCONFIG_ERROR = """
The MOZ_MYCONFIG environment variable to define the location of mozconfigs
is deprecated. If you wish to define the mozconfig path via an environment
variable, use MOZCONFIG instead.
""".strip()

MOZCONFIG_LEGACY_PATH_ERROR = """
You currently have a mozconfig at %s. This implicit location is no longer
supported. Please move it to %s/.mozconfig or set an explicit path
via the $MOZCONFIG environment variable.
""".strip()

DEFAULT_TOPSRCDIR_PATHS = (".mozconfig", "mozconfig")
DEPRECATED_TOPSRCDIR_PATHS = ("mozconfig.sh", "myconfig.sh")
DEPRECATED_HOME_PATHS = (".mozconfig", ".mozconfig.sh", ".mozmyconfig.sh")


class MozconfigFindException(Exception):
    """Raised when a mozconfig location is not defined properly."""


class MozconfigBuilder(object):
    def __init__(self):
        self._lines = []

    def append(self, block):
        self._lines.extend([line.strip() for line in block.split("\n") if line.strip()])

    def generate(self):
        return "".join(line + "\n" for line in self._lines)


def find_mozconfig(topsrcdir: Union[str, Path], env=os.environ):
    """Find the active mozconfig file for the current environment.

    This emulates the logic in mozconfig-find.

    1) If ENV[MOZCONFIG] is set, use that
    2) If $TOPSRCDIR/mozconfig or $TOPSRCDIR/.mozconfig exists, use it.
    3) If both exist or if there are legacy locations detected, error out.

    The absolute path to the found mozconfig will be returned on success.
    None will be returned if no mozconfig could be found. A
    MozconfigFindException will be raised if there is a bad state,
    including conditions from #3 above.
    """
    topsrcdir = Path(topsrcdir)

    # Check for legacy methods first.
    if "MOZ_MYCONFIG" in env:
        raise MozconfigFindException(MOZ_MYCONFIG_ERROR)

    env_path = env.get("MOZCONFIG", None) or None

    if env_path is not None:
        env_path = Path(env_path)

    if env_path is not None:
        if not env_path.is_absolute():
            potential_roots = [topsrcdir, Path.cwd()]
            # Attempt to eliminate duplicates for e.g.
            # self.topsrcdir == Path.cwd().
            potential_roots_strings = set(str(p.resolve()) for p in potential_roots)
            existing = [
                root
                for root in potential_roots_strings
                if (Path(root) / env_path).exists()
            ]
            if len(existing) > 1:
                # There are multiple files, but we might have a setup like:
                #
                # somedirectory/
                #   srcdir/
                #   objdir/
                #
                # MOZCONFIG=../srcdir/some/path/to/mozconfig
                #
                # and be configuring from the objdir.  So even though we
                # have multiple existing files, they are actually the same
                # file.
                mozconfigs = [root / env_path for root in existing]
                if not all(
                    map(
                        lambda p1, p2: filecmp.cmp(p1, p2, shallow=False),
                        mozconfigs[:-1],
                        mozconfigs[1:],
                    )
                ):
                    raise MozconfigFindException(
                        "MOZCONFIG environment variable refers to a path that "
                        + "exists in more than one of "
                        + ", ".join(potential_roots_strings)
                        + ". Remove all but one."
                    )
            elif not existing:
                raise MozconfigFindException(
                    "MOZCONFIG environment variable refers to a path that "
                    + "does not exist in any of "
                    + ", ".join(potential_roots_strings)
                )

            env_path = existing[0] / env_path
        elif not env_path.exists():  # non-relative path
            raise MozconfigFindException(
                "MOZCONFIG environment variable refers to a path that "
                f"does not exist: {env_path}"
            )

        if not env_path.is_file():
            raise MozconfigFindException(
                "MOZCONFIG environment variable refers to a " f"non-file: {env_path}"
            )

    srcdir_paths = [topsrcdir / p for p in DEFAULT_TOPSRCDIR_PATHS]
    existing = [p for p in srcdir_paths if p.is_file()]

    if env_path is None and len(existing) > 1:
        raise MozconfigFindException(
            "Multiple default mozconfig files "
            "present. Remove all but one. " + ", ".join(str(p) for p in existing)
        )

    path = None

    if env_path is not None:
        path = env_path
    elif len(existing):
        assert len(existing) == 1
        path = existing[0]

    if path is not None:
        return Path.cwd() / path

    deprecated_paths = [topsrcdir / s for s in DEPRECATED_TOPSRCDIR_PATHS]

    home = env.get("HOME", None)
    if home is not None:
        home = Path(home)
        deprecated_paths.extend([home / s for s in DEPRECATED_HOME_PATHS])

    for path in deprecated_paths:
        if path.exists():
            raise MozconfigFindException(
                MOZCONFIG_LEGACY_PATH_ERROR % (path, topsrcdir)
            )

    return None