diff options
Diffstat (limited to 'python/mozboot/mozboot/mozconfig.py')
-rw-r--r-- | python/mozboot/mozboot/mozconfig.py | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/python/mozboot/mozboot/mozconfig.py b/python/mozboot/mozboot/mozconfig.py new file mode 100644 index 0000000000..a1ae4c8523 --- /dev/null +++ b/python/mozboot/mozboot/mozconfig.py @@ -0,0 +1,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 |