pw_package: Add stm32cube package

This downloads the correct repos for every stm32 family from ST's
github.

Each family will need its own build files and integrations because the
headers and API's change slightly between product families.

Most code (UART, USART, etc.) can be shared between families with the
only difference being the header included.

Change-Id: I7f8be09671e65b85aa34e31ea74240f9bddf1fab
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/42731
Commit-Queue: Varun Sharma <vars@google.com>
Reviewed-by: Rob Mohr <mohrr@google.com>
Reviewed-by: Ali Zhang <alizhang@google.com>
diff --git a/pw_package/py/BUILD.gn b/pw_package/py/BUILD.gn
index dde870b..cc385c7 100644
--- a/pw_package/py/BUILD.gn
+++ b/pw_package/py/BUILD.gn
@@ -25,6 +25,7 @@
     "pw_package/packages/__init__.py",
     "pw_package/packages/arduino_core.py",
     "pw_package/packages/nanopb.py",
+    "pw_package/packages/stm32cube.py",
     "pw_package/pigweed_packages.py",
   ]
   pylintrc = "$dir_pigweed/.pylintrc"
diff --git a/pw_package/py/pw_package/git_repo.py b/pw_package/py/pw_package/git_repo.py
index fa9f129..5b938a9 100644
--- a/pw_package/py/pw_package/git_repo.py
+++ b/pw_package/py/pw_package/git_repo.py
@@ -41,10 +41,14 @@
 
 class GitRepo(pw_package.package_manager.Package):
     """Install and check status of Git repository-based packages."""
-    def __init__(self, url, commit, *args, **kwargs):
+    def __init__(self, url, *args, commit='', tag='', **kwargs):
         super().__init__(*args, **kwargs)
+        if not (commit or tag):
+            raise ValueError('git repo must specify a commit or tag')
+
         self._url = url
         self._commit = commit
+        self._tag = tag
 
     def status(self, path: pathlib.Path) -> bool:
         if not os.path.isdir(path / '.git'):
@@ -62,8 +66,16 @@
             remote = 'https://{}{}'.format(host, url.path)
 
         commit = git_stdout('rev-parse', 'HEAD', repo=path)
+        if self._commit and self._commit != commit:
+            return False
+
+        if self._tag:
+            tag = git_stdout('describe', '--tags', repo=path)
+            if self._tag != tag:
+                return False
+
         status = git_stdout('status', '--porcelain=v1', repo=path)
-        return remote == self._url and commit == self._commit and not status
+        return remote == self._url and not status
 
     def install(self, path: pathlib.Path) -> None:
         # If already installed and at correct version exit now.
@@ -78,5 +90,9 @@
         # revision. If we later run commands that need history it will be
         # retrieved on-demand. For small repositories the effect is negligible
         # but for large repositories this should be a significant improvement.
-        git('clone', '--filter=blob:none', self._url, path)
-        git('reset', '--hard', self._commit, repo=path)
+        if self._commit:
+            git('clone', '--filter=blob:none', self._url, path)
+            git('reset', '--hard', self._commit, repo=path)
+        elif self._tag:
+            git('clone', '-b', self._tag, '--filter=blob:none', self._url,
+                path)
diff --git a/pw_package/py/pw_package/package_manager.py b/pw_package/py/pw_package/package_manager.py
index 3f1cc80..1254121 100644
--- a/pw_package/py/pw_package/package_manager.py
+++ b/pw_package/py/pw_package/package_manager.py
@@ -66,11 +66,8 @@
 _PACKAGES: Dict[str, Package] = {}
 
 
-def register(package_class: type, name: str = None) -> None:
-    if name:
-        obj = package_class(name)
-    else:
-        obj = package_class()
+def register(package_class: type, *args, **kwargs) -> None:
+    obj = package_class(*args, **kwargs)
     _PACKAGES[obj.name] = obj
 
 
diff --git a/pw_package/py/pw_package/packages/arduino_core.py b/pw_package/py/pw_package/packages/arduino_core.py
index 994c95c..c630c93 100644
--- a/pw_package/py/pw_package/packages/arduino_core.py
+++ b/pw_package/py/pw_package/packages/arduino_core.py
@@ -137,4 +137,5 @@
 
 
 for arduino_core_name in core_installer.supported_cores():
-    pw_package.package_manager.register(ArduinoCore, name=arduino_core_name)
+    pw_package.package_manager.register(ArduinoCore,
+                                        core_name=arduino_core_name)
diff --git a/pw_package/py/pw_package/packages/stm32cube.py b/pw_package/py/pw_package/packages/stm32cube.py
new file mode 100644
index 0000000..2d53175
--- /dev/null
+++ b/pw_package/py/pw_package/packages/stm32cube.py
@@ -0,0 +1,148 @@
+# Copyright 2021 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.
+"""Install and check status of stm32cube."""
+
+import pathlib
+from typing import Sequence
+
+import pw_package.git_repo
+import pw_package.package_manager
+
+# Compatible versions are listed on the README.md of each hal_driver repo
+_STM32CUBE_VERSIONS = {
+    "f0": {
+        "hal_driver_tag": "v1.7.5",
+        "cmsis_device_tag": "v2.3.5",
+        "cmsis_core_tag": "v5.4.0_cm0",
+    },
+    "f1": {
+        "hal_driver_tag": "v1.1.7",
+        "cmsis_device_tag": "v4.3.2",
+        "cmsis_core_tag": "v5.4.0_cm3",
+    },
+    "f2": {
+        "hal_driver_tag": "v1.2.6",
+        "cmsis_device_tag": "v2.2.4",
+        "cmsis_core_tag": "v5.4.0_cm3",
+    },
+    "f3": {
+        "hal_driver_tag": "v1.5.5",
+        "cmsis_device_tag": "v2.3.5",
+        "cmsis_core_tag": "v5.4.0_cm4",
+    },
+    "f4": {
+        "hal_driver_tag": "v1.7.12",
+        "cmsis_device_tag": "v2.6.6",
+        "cmsis_core_tag": "v5.4.0_cm4",
+    },
+    "f7": {
+        "hal_driver_tag": "v1.2.9",
+        "cmsis_device_tag": "v1.2.6",
+        "cmsis_core_tag": "v5.4.0_cm7",
+    },
+    "g0": {
+        "hal_driver_tag": "v1.4.1",
+        "cmsis_device_tag": "v1.4.0",
+        "cmsis_core_tag": "v5.6.0_cm0",
+    },
+    "g4": {
+        "hal_driver_tag": "v1.2.1",
+        "cmsis_device_tag": "v1.2.1",
+        "cmsis_core_tag": "v5.6.0_cm4",
+    },
+    "h7": {
+        "hal_driver_tag": "v1.10.0",
+        "cmsis_device_tag": "v1.10.0",
+        "cmsis_core_tag": "v5.6.0",
+    },
+    "l0": {
+        "hal_driver_tag": "v1.10.4",
+        "cmsis_device_tag": "v1.9.1",
+        "cmsis_core_tag": "v4.5_cm0",
+    },
+    "l1": {
+        "hal_driver_tag": "v1.4.3",
+        "cmsis_device_tag": "v2.3.1",
+        "cmsis_core_tag": "v5.4.0_cm3",
+    },
+    "l4": {
+        "hal_driver_tag": "v1.13.0",
+        "cmsis_device_tag": "v1.7.1",
+        "cmsis_core_tag": "v5.6.0_cm4",
+    },
+    "l5": {
+        "hal_driver_tag": "v1.0.4",
+        "cmsis_device_tag": "v1.0.4",
+        "cmsis_core_tag": "v5.6.0_cm33",
+    },
+    "wb": {
+        "hal_driver_tag": "v1.8.0",
+        "cmsis_device_tag": "v1.8.0",
+        "cmsis_core_tag": "v5.4.0_cm4",
+    },
+    "wl": {
+        "hal_driver_tag": "v1.0.0",
+        "cmsis_device_tag": "v1.0.0",
+        "cmsis_core_tag": "V5.6.0_cm4",
+    },
+}
+
+
+class Stm32Cube(pw_package.package_manager.Package):
+    """Install and check status of stm32cube."""
+    def __init__(self, family, tags, *args, **kwargs):
+        super().__init__(*args, name=f'stm32cube{family}', **kwargs)
+
+        st_github_url = 'https://github.com/STMicroelectronics'
+
+        self._hal_driver = pw_package.git_repo.GitRepo(
+            name=f'stm32{family}xx_hal_driver',
+            url=f'{st_github_url}/stm32{family}xx_hal_driver.git',
+            tag=tags['hal_driver_tag'],
+        )
+
+        self._cmsis_core = pw_package.git_repo.GitRepo(
+            name='cmsis_core',
+            url=f'{st_github_url}/cmsis_core.git',
+            tag=tags['cmsis_core_tag'],
+        )
+
+        self._cmsis_device = pw_package.git_repo.GitRepo(
+            name=f'cmsis_device_{family}',
+            url=f'{st_github_url}/cmsis_device_{family}.git',
+            tag=tags['cmsis_device_tag'],
+        )
+
+    def install(self, path: pathlib.Path) -> None:
+        self._hal_driver.install(path / self._hal_driver.name)
+        self._cmsis_core.install(path / self._cmsis_core.name)
+        self._cmsis_device.install(path / self._cmsis_device.name)
+
+    def status(self, path: pathlib.Path) -> bool:
+        return (
+            self._hal_driver.status(path / self._hal_driver.name),
+            self._cmsis_core.status(path / self._cmsis_core.name),
+            self._cmsis_device.status(path / self._cmsis_device.name),
+        ) == (True, True, True)
+
+    def info(self, path: pathlib.Path) -> Sequence[str]:
+        return (
+            f'{self.name} installed in: {path}',
+            "Enable by running 'gn args out' and adding this line:",
+            f'  dir_pw_third_party_{self.name} = "{path}"',
+        )
+
+
+for st_family, st_tags in _STM32CUBE_VERSIONS.items():
+    pw_package.package_manager.register(Stm32Cube, st_family, st_tags)
diff --git a/pw_package/py/pw_package/pigweed_packages.py b/pw_package/py/pw_package/pigweed_packages.py
index 6c39266..5c90120 100644
--- a/pw_package/py/pw_package/pigweed_packages.py
+++ b/pw_package/py/pw_package/pigweed_packages.py
@@ -18,6 +18,7 @@
 from pw_package import package_manager
 from pw_package.packages import nanopb
 from pw_package.packages import arduino_core  # pylint: disable=unused-import
+from pw_package.packages import stm32cube  # pylint: disable=unused-import
 
 
 def initialize():