Reduce duplication in the Bazel rules

Change-Id: I68c700201f35eb150d606b8302cca56f44014590
Reviewed-on: https://pigweed-internal-review.git.corp.google.com/c/pigweed/showcase/rp2/+/73878
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Keir Mierle <keir@google.com>
diff --git a/apps/blinky/BUILD.bazel b/apps/blinky/BUILD.bazel
index 9d42b56..7005d8a 100644
--- a/apps/blinky/BUILD.bazel
+++ b/apps/blinky/BUILD.bazel
@@ -12,10 +12,10 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
 load("@pigweed//targets/host_device_simulator:transition.bzl", "host_device_simulator_binary")
 load("@pigweed//targets/rp2040:flash.bzl", "flash_rp2040")
 load("//targets/rp2:binary.bzl", "rp2040_binary", "rp2350_binary")
+load("//tools:tools.bzl", "sense_device_console", "sense_device_script", "sense_host_console",)
 
 package(default_visibility = ["//visibility:public"])
 
@@ -56,134 +56,61 @@
     binary = ":blinky",
 )
 
-SIMULATOR_CONSOLE_COMMON_ARGS = [
-    # This arg lets us skip manual port selection.
-    "--socket",
-    "default",
-    "--config-file",
-    "$(rootpath //:pw_console_config)",
-]
-
-# Connect to a running blinky host_device_simulator_binary with:
-#   bazel run //apps/blinky:simulator_console
-native_binary(
+sense_host_console(
     name = "simulator_console",
-    src = "//tools:console",
-    args = SIMULATOR_CONSOLE_COMMON_ARGS,
-    data = [
-        "//:pw_console_config",
-    ],
+    binary = ":simulator_blinky",
 )
 
-native_binary(
+sense_host_console(
     name = "simulator_webconsole",
-    src = "//tools:console",
-    args = SIMULATOR_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        "//:pw_console_config",
-    ],
+    binary = ":simulator_blinky",
+    extra_args = ["--browser"],
 )
 
-RP2040_CONSOLE_COMMON_ARGS = [
-    "-b",
-    "115200",
-    "--token-databases",
-    "$(rootpath //apps/blinky:rp2040_blinky.elf)",
-    "--config-file",
-    "$(rootpath //:pw_console_config)",
-]
-
-RP2350_CONSOLE_COMMON_ARGS = [
-    a.replace("rp2040", "rp2350")
-    for a in RP2040_CONSOLE_COMMON_ARGS
-]
-
-native_binary(
+sense_device_console(
     name = "rp2040_console",
-    src = "//tools:console",
-    args = RP2040_CONSOLE_COMMON_ARGS,
-    data = [
-        ":rp2040_blinky.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2040_blinky.elf",
 )
 
-native_binary(
-    name = "rp2350_console",
-    src = "//tools:console",
-    args = RP2350_CONSOLE_COMMON_ARGS,
-    data = [
-        ":rp2350_blinky.elf",
-        "//:pw_console_config",
-    ],
-)
-
-native_binary(
+sense_device_console(
     name = "rp2040_webconsole",
-    src = "//tools:console",
-    args = RP2040_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        ":rp2040_blinky.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2040_blinky.elf",
+    extra_args = ["--browser"]
 )
 
-native_binary(
+sense_device_console(
+    name = "rp2350_console",
+    binary = ":rp2350_blinky.elf",
+)
+
+sense_device_console(
     name = "rp2350_webconsole",
-    src = "//tools:console",
-    args = RP2350_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        ":rp2350_blinky.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2350_blinky.elf",
+    extra_args = ["--browser"]
 )
 
-native_binary(
+sense_device_script(
     name = "rp2040_example_script",
     src = "//tools:example_script",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/blinky:rp2040_blinky.elf)",
-    ],
-    data = [
-        ":rp2040_blinky.elf",
-    ],
+    binary = ":rp2040_blinky.elf",
 )
 
-native_binary(
+sense_device_script(
     name = "rp2350_example_script",
     src = "//tools:example_script",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/blinky:rp2350_blinky.elf)",
-    ],
-    data = [
-        ":rp2350_blinky.elf",
-    ],
+    binary = ":rp2350_blinky.elf",
 )
 
-native_binary(
+sense_device_script(
     name = "rp2040_toggle_blinky",
     src = "//tools:toggle_blinky",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/blinky:rp2040_blinky.elf)",
-    ],
-    data = [
-        ":rp2040_blinky.elf",
-    ],
+    binary = ":rp2040_blinky.elf",
 )
 
-native_binary(
+sense_device_script(
     name = "rp2350_toggle_blinky",
     src = "//tools:toggle_blinky",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/blinky:rp2350_blinky.elf)",
-    ],
-    data = [
-        ":rp2350_blinky.elf",
-    ],
+    binary = ":rp2350_blinky.elf",
 )
 
 alias(
@@ -196,6 +123,7 @@
     rp2040_binary = "rp2040_blinky.elf",
 )
 
+# Note: Despite the name, the rule works for the 2350.
 flash_rp2040(
     name = "flash_rp2350",
     rp2040_binary = "rp2350_blinky.elf",
diff --git a/apps/factory/BUILD.bazel b/apps/factory/BUILD.bazel
index 69509f7..f52c8f0 100644
--- a/apps/factory/BUILD.bazel
+++ b/apps/factory/BUILD.bazel
@@ -12,7 +12,6 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
 load(
     "@pigweed//pw_protobuf_compiler:pw_proto_library.bzl",
     "nanopb_proto_library",
@@ -21,6 +20,7 @@
 load("@pigweed//targets/rp2040:flash.bzl", "flash_rp2040")
 load("@rules_python//python:proto.bzl", "py_proto_library")
 load("//targets/rp2:binary.bzl", "rp2040_binary")
+load("//tools:tools.bzl", "sense_device_console", "sense_device_script")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -49,39 +49,20 @@
     ],
 )
 
-# Create an rp2040 flashable ELF
 rp2040_binary(
     name = "rp2040.elf",
     binary = ":factory",
 )
 
-native_binary(
+sense_device_console(
     name = "rp2040_console",
-    src = "//tools:console",
-    args = [
-        "-b",
-        "115200",
-        "--token-databases",
-        "$(rootpath //apps/factory:rp2040.elf)",
-        "--config-file",
-        "$(rootpath //:pw_console_config)",
-    ],
-    data = [
-        ":rp2040.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2040.elf",
 )
 
-native_binary(
+sense_device_script(
     name = "rp2040_factory_test",
     src = "//tools:factory",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/factory:rp2040.elf)",
-    ],
-    data = [
-        ":rp2040.elf",
-    ],
+    binary = ":rp2040.elf",
 )
 
 flash_rp2040(
diff --git a/apps/production/BUILD.bazel b/apps/production/BUILD.bazel
index 6f62ee4..8ca7488 100644
--- a/apps/production/BUILD.bazel
+++ b/apps/production/BUILD.bazel
@@ -12,11 +12,11 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
 load("@pigweed//pw_build:compatibility.bzl", "host_backend_alias")
 load("@pigweed//targets/host_device_simulator:transition.bzl", "host_device_simulator_binary")
 load("@pigweed//targets/rp2040:flash.bzl", "flash_rp2040")
 load("//targets/rp2:binary.bzl", "rp2040_binary", "rp2350_binary")
+load("//tools:tools.bzl", "sense_device_console", "sense_host_console",)
 
 package(default_visibility = ["//visibility:public"])
 
@@ -82,110 +82,37 @@
     binary = ":production",
 )
 
-SIMULATOR_CONSOLE_COMMON_ARGS = [
-    # This arg lets us skip manual port selection.
-    "--socket",
-    "default",
-    "--config-file",
-    "$(rootpath //:pw_console_config)",
-]
-
-# Connect to a running host_device_simulator_binary with:
-#   bazel run //apps/production:simulator_console
-native_binary(
+sense_host_console(
     name = "simulator_console",
-    src = "//tools:console",
-    args = SIMULATOR_CONSOLE_COMMON_ARGS,
-    data = [
-        "//:pw_console_config",
-    ],
+    binary = ":simulator",
 )
 
-native_binary(
+sense_host_console(
     name = "simulator_webconsole",
-    src = "//tools:console",
-    args = SIMULATOR_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        "//:pw_console_config",
-    ],
+    binary = ":simulator",
+    extra_args = ["--browser"],
 )
 
-RP2040_CONSOLE_COMMON_ARGS = [
-    "-b",
-    "115200",
-    "--token-databases",
-    "$(rootpath //apps/production:rp2040.elf)",
-    "--config-file",
-    "$(rootpath //:pw_console_config)",
-]
-
-RP2350_CONSOLE_COMMON_ARGS = [
-    a.replace("rp2040", "rp2350")
-    for a in RP2040_CONSOLE_COMMON_ARGS
-]
-
-native_binary(
+sense_device_console(
     name = "rp2040_console",
-    src = "//tools:console",
-    args = RP2040_CONSOLE_COMMON_ARGS,
-    data = [
-        ":rp2040.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2040.elf",
 )
 
-native_binary(
-    name = "rp2350_console",
-    src = "//tools:console",
-    args = RP2350_CONSOLE_COMMON_ARGS,
-    data = [
-        ":rp2350.elf",
-        "//:pw_console_config",
-    ],
-)
-
-native_binary(
+sense_device_console(
     name = "rp2040_webconsole",
-    src = "//tools:console",
-    args = RP2040_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        ":rp2040.elf",
-        "//:pw_console_config",
-    ],
+    binary = ":rp2040.elf",
+    extra_args = ["--browser"]
 )
 
-native_binary(
+sense_device_console(
+    name = "rp2350_console",
+    binary = ":rp2350.elf",
+)
+
+sense_device_console(
     name = "rp2350_webconsole",
-    src = "//tools:console",
-    args = RP2350_CONSOLE_COMMON_ARGS + ["--browser"],
-    data = [
-        ":rp2350.elf",
-        "//:pw_console_config",
-    ],
-)
-
-native_binary(
-    name = "rp2040_air_measure",
-    src = "//tools:air_measure",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/production:rp2040.elf)",
-    ],
-    data = [
-        ":rp2040.elf",
-    ],
-)
-
-native_binary(
-    name = "rp2350_air_measure",
-    src = "//tools:air_measure",
-    args = [
-        "--token-databases",
-        "$(rootpath //apps/production:rp2350.elf)",
-    ],
-    data = [
-        ":rp2350.elf",
-    ],
+    binary = ":rp2350.elf",
+    extra_args = ["--browser"]
 )
 
 alias(
@@ -198,6 +125,7 @@
     rp2040_binary = "rp2040.elf",
 )
 
+# Note: Despite the name, the rule works for the 2350.
 flash_rp2040(
     name = "flash_rp2350",
     rp2040_binary = "rp2350.elf",
diff --git a/tools/tools.bzl b/tools/tools.bzl
new file mode 100644
index 0000000..653bdf6
--- /dev/null
+++ b/tools/tools.bzl
@@ -0,0 +1,93 @@
+# 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.
+
+load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
+
+def sense_host_console(name, binary, extra_args = []):
+    """Create a host binary console run target
+
+    Args:
+    name: target name
+    binary: target binary the console is for
+    extra_args: additional arguments added to the console invocation
+    """
+    native_binary(
+        name = name,
+        src = "//tools:console",
+        args = [
+            # This arg lets us skip manual port selection.
+            "--socket",
+            "default",
+            "--config-file",
+            "$(rootpath //:pw_console_config)",
+        ] + extra_args,
+        data = [
+            binary,
+            "//:pw_console_config",
+        ],
+    )
+
+def sense_device_console(name, binary, extra_args = []):
+    """Create a device binary console run target
+
+    Makes running a console for a binary easy, and ensures the associated binary is
+    up to date (but does not flash the device).
+
+    Args:
+    name: target name
+    binary: target binary the console is for
+    extra_args: additional arguments added to the console invocation
+    """
+    native_binary(
+        name = name,
+        src = "//tools:console",
+        args = [
+            "-b",
+            "115200",
+            "--token-databases",
+            "$(rootpath " + binary + ")",
+            "--config-file",
+            "$(rootpath //:pw_console_config)",
+        ] + extra_args,
+        data = [
+            binary,
+            "//:pw_console_config",
+        ],
+    )
+
+def sense_device_script(name, src, binary, extra_args = []):
+    """Create a device binary script run target
+
+    Makes running a script for a binary easy, and ensures the associated binary is
+    up to date (but does not flash the device).
+
+    Args:
+    name: target name
+    src: script target
+    binary: target binary the console is for
+    extra_args: additional arguments added to the console invocation
+    """
+    native_binary(
+        name = name,
+        src = src,
+        args = [
+            "-b",
+            "115200",
+            "--token-databases",
+            "$(rootpath " + binary + ")",
+        ] + extra_args,
+        data = [
+            binary,
+        ],
+    )
\ No newline at end of file