xref: /aosp_15_r20/external/pigweed/pw_env_setup/py/pw_env_setup/config_file.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2023 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Reads and parses the Pigweed config file.
15
16See also https://pigweed.dev/seed/0101-pigweed.json.html.
17"""
18
19from __future__ import annotations
20
21from collections.abc import Mapping
22import json
23import os
24from pathlib import Path
25from typing import Any
26
27try:
28    # This will only succeed when using config_file from Bazel.
29    from python.runfiles import runfiles  # type: ignore
30except (ImportError, ModuleNotFoundError):
31    runfiles = None
32
33
34def _resolve_env(env: Mapping[str, str] | None) -> Mapping[str, str]:
35    if env:
36        return env
37    return os.environ
38
39
40def _get_project_root(env: Mapping[str, str]) -> Path:
41    for var in ('PW_PROJECT_ROOT', 'PW_ROOT'):
42        if var in env:
43            return Path(env[var])
44    raise ValueError('environment variable PW_PROJECT_ROOT not set')
45
46
47def _pw_env_substitute(env: Mapping[str, str], string: Any) -> str:
48    if not isinstance(string, str):
49        return string
50
51    # Substitute in environment variables based on $pw_env{VAR_NAME} tokens.
52    for key, value in env.items():
53        string = string.replace('$pw_env{' + key + '}', value)
54
55    if '$pw_env{' in string:
56        raise ValueError(f'Unresolved $pw_env{{...}} in JSON string: {string}')
57
58    return string
59
60
61def path(env: Mapping[str, str] | None = None) -> Path:
62    """Return the path where pigweed.json should reside."""
63    env = _resolve_env(env)
64    return _get_project_root(env=env) / 'pigweed.json'
65
66
67def path_from_runfiles() -> Path:
68    r = runfiles.Create()
69    location = r.Rlocation("pigweed.json")
70    if location is None:
71        # Failed to find pigweed.json
72        raise ValueError("Failed to find pigweed.json")
73
74    return Path(location)
75
76
77def load(env: Mapping[str, str] | None = None) -> dict:
78    """Load pigweed.json if it exists and return the contents."""
79    if env is None and runfiles is not None:
80        config_path = path_from_runfiles()
81    else:
82        env = _resolve_env(env)
83        config_path = path(env=env)
84
85    if not os.path.isfile(config_path):
86        return {}
87
88    def hook(obj):
89        out = {}
90        for key, val in obj.items():
91            out[key] = _pw_env_substitute(env, val)
92        return out
93
94    with open(config_path, 'r') as file:
95        return json.load(file, object_hook=hook)
96