pw_build: Subtarget aliases for Python packages

- Create top-level Python package substarget aliases when the Python
  package name matches the directory name. For example, this allows
  referring to "//foo:foo.tests" as "//foo:tests".
- Update and fix docs.

Change-Id: I1ee78c234c15e0799a47fb6ed4691e4a94a2c169
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/40180
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 550f592..038d9a2 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -32,6 +32,21 @@
   "_build_wheel",
 ]
 
+# Create aliases for subsargets when the target name matches the directory name.
+# This allows //foo:foo.tests to be accessed as //foo:tests, for example.
+template("_pw_create_aliases_if_name_matches_directory") {
+  not_needed([ "invoker" ])
+
+  if (get_label_info(":$target_name", "name") ==
+      get_path_info(get_label_info(":$target_name", "dir"), "name")) {
+    foreach(subtarget, pw_python_package_subtargets) {
+      group(subtarget) {
+        public_deps = [ ":${invoker.target_name}.$subtarget" ]
+      }
+    }
+  }
+}
+
 # Internal template that runs Mypy.
 template("_pw_python_static_analysis_mypy") {
   pw_python_action(target_name) {
@@ -612,6 +627,9 @@
   group("$target_name.tests") {
     deps = _test_targets
   }
+
+  _pw_create_aliases_if_name_matches_directory(target_name) {
+  }
 }
 
 # Declares a group of Python packages or other Python groups. pw_python_groups
@@ -631,15 +649,18 @@
 
   foreach(subtarget, pw_python_package_subtargets) {
     group("$target_name.$subtarget") {
-      deps = []
+      public_deps = []
       foreach(dep, _python_deps) {
         # Split out the toolchain to support deps with a toolchain specified.
         _target = get_label_info(dep, "label_no_toolchain")
         _toolchain = get_label_info(dep, "toolchain")
-        deps += [ "$_target.$subtarget($_toolchain)" ]
+        public_deps += [ "$_target.$subtarget($_toolchain)" ]
       }
     }
   }
+
+  _pw_create_aliases_if_name_matches_directory(target_name) {
+  }
 }
 
 # Declares Python scripts or tests that are not part of a Python package.
@@ -667,6 +688,9 @@
     _pw_standalone = true
     forward_variables_from(invoker, _supported_variables)
   }
+
+  _pw_create_aliases_if_name_matches_directory(target_name) {
+  }
 }
 
 # Represents a list of Python requirements, as in a requirements.txt.
@@ -721,4 +745,7 @@
     group("$target_name.$subtarget") {
     }
   }
+
+  _pw_create_aliases_if_name_matches_directory(target_name) {
+  }
 }
diff --git a/pw_build/python.rst b/pw_build/python.rst
index fc201eb..09b92a2 100644
--- a/pw_build/python.rst
+++ b/pw_build/python.rst
@@ -21,10 +21,30 @@
 - ``<name>.install`` - Installs the package
 - ``<name>.wheel`` - Builds a Python wheel
 
+GN permits using abbreviated labels when the target name matches the directory
+name (e.g. ``//foo`` for ``//foo:foo``). For consistency with this, Python
+package subtargets are aliased to the directory when the target name is the
+same as the directory. For example, these two labels are equivalent:
+
+.. code-block::
+
+  //path/to/my_python_package:my_python_package.tests
+  //path/to/my_python_package:tests
+
 Arguments
 ---------
 - ``setup`` - List of setup file paths (setup.py or pyproject.toml & setup.cfg),
   which must all be in the same directory.
+- ``generate_setup``: As an alternative to ``setup``, generate setup files with
+  the keywords in this scope. ``name`` is required. For example:
+
+  .. code-block::
+
+    generate_setup = {
+      name = "a_nifty_package"
+      version = "1.2a"
+    }
+
 - ``sources`` - Python sources files in the package.
 - ``tests`` - Test files for this Python package.
 - ``python_deps`` - Dependencies on other pw_python_packages in the GN build.
@@ -32,7 +52,7 @@
 - ``other_deps`` - Dependencies on GN targets that are not pw_python_packages.
 - ``inputs`` - Other files to track, such as package_data.
 - ``proto_library`` - A pw_proto_library target to embed in this Python package.
-  generate_setup is required in place of setup if proto_library is used. See
+  ``generate_setup`` is required in place of setup if proto_library is used. See
   :ref:`module-pw_protobuf_compiler-add-to-python-package`.
 - ``static_analysis`` List of static analysis tools to run; ``"*"`` (default)
   runs all tools. The supported tools are ``"mypy"`` and ``"pylint"``.
@@ -95,7 +115,7 @@
 
   pw_mirror_tree("my_wheels") {
     path_data_keys = [ "pw_python_package_wheels" ]
-    deps = [ ":python_packages" ]
+    deps = [ ":python_packages.wheel" ]
     directory = "$root_out_dir/the_wheels"
   }