pw_env_setup: Bazel support for config_file

Bug: 340328100
Change-Id: I88ccc3a2ad8b0365551a7f8952c6c2eda644a62b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/209913
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Commit-Queue: Ted Pudlik <tpudlik@google.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/.bazelrc b/.bazelrc
index 74b0574..7898c67 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -100,6 +100,8 @@
 common --@stm32f4xx_hal_driver//:cmsis_device=@cmsis_device_f4//:cmsis_device
 common --@stm32f4xx_hal_driver//:cmsis_init=@cmsis_device_f4//:default_cmsis_init
 
+common --//pw_env_setup/py:pigweed_json=//:pigweed.json
+
 # Config for the stm32f429i_disc1_stm32cube platform.
 #
 # TODO: b/301334234 - Make the platform set the flags below.
diff --git a/BUILD.bazel b/BUILD.bazel
index 0bb7113..ae4a737 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -29,6 +29,9 @@
 )
 
 exports_files(
-    ["tsconfig.json"],
+    [
+        "pigweed.json",
+        "tsconfig.json",
+    ],
     visibility = [":__subpackages__"],
 )
diff --git a/pw_env_setup/BUILD.bazel b/pw_env_setup/BUILD.bazel
index 1e2525c..486d1ed 100644
--- a/pw_env_setup/BUILD.bazel
+++ b/pw_env_setup/BUILD.bazel
@@ -11,8 +11,3 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-
-exports_files([
-    "py/pw_env_setup/cipd_setup/.cipd_version",
-    "py/pw_env_setup/cipd_setup/.cipd_version.digests",
-] + glob(["py/**/*.json"]))
diff --git a/pw_env_setup/py/BUILD.bazel b/pw_env_setup/py/BUILD.bazel
new file mode 100644
index 0000000..7fab2c0
--- /dev/null
+++ b/pw_env_setup/py/BUILD.bazel
@@ -0,0 +1,53 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+exports_files([
+    "pw_env_setup/cipd_setup/.cipd_version",
+    "pw_env_setup/cipd_setup/.cipd_version.digests",
+] + glob(["pw_env_setup/**/*.json"]))
+
+# Downstream projects should set this label flag to point to their pigweed.json
+# file.
+label_flag(
+    name = "pigweed_json",
+    # We don't provide a default pigweed.json. Every project needs their own!
+    build_setting_default = "//pw_build:unspecified_backend",
+)
+
+py_library(
+    name = "config_file",
+    srcs = [
+        "pw_env_setup/__init__.py",
+        "pw_env_setup/config_file.py",
+    ],
+    data = [
+        ":pigweed_json",
+    ],
+    imports = ["."],
+    deps = [
+        "@rules_python//python/runfiles",
+    ],
+)
+
+py_test(
+    name = "config_file_test",
+    srcs = ["config_file_test.py"],
+    deps = [
+        ":config_file",
+    ],
+)
diff --git a/pw_env_setup/py/BUILD.gn b/pw_env_setup/py/BUILD.gn
index e737243..bbb5f09 100644
--- a/pw_env_setup/py/BUILD.gn
+++ b/pw_env_setup/py/BUILD.gn
@@ -47,6 +47,7 @@
     "pw_env_setup/zephyr_sdk_actions.py",
   ]
   tests = [
+    "config_file_test.py",
     "cipd_setup_update_test.py",
     "environment_test.py",
     "json_visitor_test.py",
diff --git a/pw_env_setup/py/config_file_test.py b/pw_env_setup/py/config_file_test.py
new file mode 100644
index 0000000..b93b4cb
--- /dev/null
+++ b/pw_env_setup/py/config_file_test.py
@@ -0,0 +1,26 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Tests for the config_file module."""
+
+import unittest
+
+from pw_env_setup import config_file
+
+
+class TestConfigFile(unittest.TestCase):
+    def test_config_load(self):
+        config = config_file.load()
+        # We should have loaded upstream Pigweed's pigweed.json, which will
+        # contain a top-level "pw" section.
+        self.assertIn(config, "pw")
diff --git a/pw_env_setup/py/pw_env_setup/config_file.py b/pw_env_setup/py/pw_env_setup/config_file.py
index 56bc142..b827eeb 100644
--- a/pw_env_setup/py/pw_env_setup/config_file.py
+++ b/pw_env_setup/py/pw_env_setup/config_file.py
@@ -19,6 +19,12 @@
 import json
 import os
 
+try:
+    # This will only succeed when using config_file from Bazel.
+    from rules_python.python.runfiles import runfiles  # type: ignore
+except (ImportError, ModuleNotFoundError):
+    runfiles = None
+
 
 def _resolve_env(env):
     if env:
@@ -53,10 +59,24 @@
     return os.path.join(_get_project_root(env=env), 'pigweed.json')
 
 
+def path_from_runfiles():
+    r = runfiles.Create()
+    location = r.Rlocation("pigweed.json")
+    if location is None:
+        # Failed to find pigweed.json
+        raise ValueError("Failed to find pigweed.json")
+
+    return location
+
+
 def load(env=None):
     """Load pigweed.json if it exists and return the contents."""
-    env = _resolve_env(env)
-    config_path = path(env=env)
+    if env is None and runfiles is not None:
+        config_path = path_from_runfiles()
+    else:
+        env = _resolve_env(env)
+        config_path = path(env=env)
+
     if not os.path.isfile(config_path):
         return {}