1#!/usr/bin/python
2"""
3SPDX-License-Identifier: Apache-2.0
4Copyright (c) 2024 STMicroelectronics.
5This script define Stm32SerieUpdate class
6to be used by update_stm32_package.py
7"""
8
9import os
10import shutil
11import subprocess
12import re
13from pathlib import Path
14import logging
15from jinja2 import Environment, FileSystemLoader
16import ble_library
17from common_utils import common_utils
18
19STM32_CUBE_REPO_BASE = "https://github.com/STMicroelectronics/STM32Cube"
20"""GitHub URL to get STM32Cube"""
21
22SCRIPT_DIR = Path(__file__).absolute().parent
23"""Script directory."""
24
25REPO_ROOT = SCRIPT_DIR / ".."
26"""Repository root (used for input/output default folders)."""
27
28# list of created files. It is necessary to remove all of them
29# as they are fully created when applying zephyr patch
30zephyr_file_created = [
31    "CMakeLists.txt",
32    "README",
33    "drivers/include/stm32_assert.h",
34]
35
36
37def version_tuple(version):
38    """Remove 'v' in front of version and convert it to tuple,
39    so that versions can be compared
40    """
41    v = re.sub("v", r"", version)
42    return tuple(map(int, (v.split("."))))
43
44
45class Stm32SerieUpdate:
46    """class Stm32SerieUpdate"""
47
48    def __init__(
49        self,
50        stm32_serie,
51        stm32cube_repo_path,
52        noclean,
53        version_update,
54        debug,
55    ):
56        """Class Stm32SerieUpdate constructor
57
58        Args:
59            stm32_serie: stm32 serie ex:stm32f3xx
60            stm32cube_repo_path: directory path where to fetch github repo
61            noclean: boolean to clean or not github repo after update done
62            version_update: string to force a specified version to be updated
63            debug: boolean to set log debug level
64
65        Returns:
66            return previous zephyr cube version.
67
68        Raises:
69            ValueError: If stm32 serie is not recognised.
70            FileNotFoundError: If Zphyr STM32 cube path is not found
71        """
72        if not stm32_serie.startswith("stm32"):
73            raise ValueError(
74                f"Error: Unknown stm32 serie: {stm32_serie}. Must start with 'stm32'"
75            )
76
77        # Set serie variables
78        self.stm32_serie = stm32_serie
79        self.stm32_seriexx = stm32_serie + "xx"  # ex:stm32f3xx
80        self.stm32_serie_upper = stm32_serie.upper()  # ex:STM32F3
81        self.stm32_seriexx_upper = self.stm32_serie_upper + "xx"  # ex:STM32F3xx
82        self.serie = self.stm32_serie_upper[5:]
83        self.noclean = noclean
84        self.version_update = version_update
85        self.debug = debug
86        self.module_patch = f"module_{self.stm32_serie}.patch"
87
88        # #####  3 root directories to work with ########
89        # 1: STM32Cube repo Default $HOME/STM32Cube_repo
90        # 2: zephyr stm32 path  : ex: .../zephyr_project/module/hal/stm32
91        # 3: Temporary directory to construct the update
92        # (within STM32Cube repo dir)
93        self.stm32cube_repo_path = stm32cube_repo_path
94        if not self.stm32cube_repo_path.exists():
95            self.stm32cube_repo_path.mkdir()
96
97        self.zephyr_hal_stm32_path = REPO_ROOT
98        if not self.zephyr_hal_stm32_path.exists():
99            raise FileNotFoundError("Error: cannot find zephyr project")
100
101        self.stm32cube_temp = self.stm32cube_repo_path / "temp_stm32xx_update"
102        if self.stm32cube_temp.exists():
103            shutil.rmtree(
104                str(self.stm32cube_temp), onerror=common_utils.remove_readonly
105            )
106        self.stm32cube_temp.mkdir()
107
108        # subdir specific to a stm32 serie
109        self.stm32cube_serie_path = self.stm32cube_repo_path / Path(
110            "STM32Cube" + self.serie
111        )
112        self.zephyr_module_serie_path = (
113            self.zephyr_hal_stm32_path / "stm32cube" / self.stm32_seriexx
114        )
115        self.stm32cube_temp_serie = (
116            self.stm32cube_temp / "stm32cube" / self.stm32_seriexx
117        )
118        shutil.rmtree(str(self.stm32cube_temp), onerror=common_utils.remove_readonly)
119        self.stm32cube_temp_serie.mkdir(parents=True)
120
121        self.readme_file_path = self.zephyr_module_serie_path / "README"
122        self.version_tag = []
123        self.current_version = ""
124        self.update_commit = ""
125
126        if self.debug:
127            logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.DEBUG)
128            self.std_dest = None
129        else:
130            logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.INFO)
131            self.std_dest = subprocess.DEVNULL
132
133    def os_cmd(self, cmd, cwd=None, shell=False):
134        """Execute a command with subprocess.check_call()
135        Args:
136            cmd: string command to execute.
137            cwd: directory where to run command
138            shell: boolean to enable command interpretation by the shell
139
140        Returns:
141            return the returncode of the command after execution.
142        """
143        logging.debug("%s", f"{str(cmd)}      cwd:{str(cwd)}")
144
145        return subprocess.check_call(
146            cmd,
147            shell=shell,
148            stdout=self.std_dest,
149            stderr=self.std_dest,
150            cwd=cwd,
151        )
152
153    def rename_conf_template(self, path):
154        """renames hal_conf_template.h to hal_conf.h ...
155        Args:
156            path: path where to apply the files processing
157        """
158        # except for _hal_conf_template.h which is renamed
159        hal_conf_template_fullpath = Path(
160            path / (self.stm32_seriexx + "_hal_conf_template.h")
161        )
162        if hal_conf_template_fullpath.is_file():
163            hal_conf_fullpath = Path(
164                re.sub("_template", r"", str(hal_conf_template_fullpath))
165            )
166            if hal_conf_fullpath.exists():
167                hal_conf_fullpath.unlink()
168            hal_conf_template_fullpath.rename(hal_conf_fullpath)
169
170    def major_branch(self):
171        # check whether master branch exist, otherwise use main branch
172        master_branch_exist = subprocess.check_output(
173            ("git", "ls-remote", "--heads", "origin", "master"),
174            cwd=self.stm32cube_serie_path,
175        ).decode("utf-8")
176        if master_branch_exist:
177            return "master"
178        else:
179            return "main"
180
181    def clone_cube_repo(self):
182        """Clone or fetch a stm32 serie repo"""
183        repo_name = STM32_CUBE_REPO_BASE + self.serie + ".git"
184        logging.info(
185            "%s",
186            "Cloning/fetching repo "
187            + repo_name
188            + " in "
189            + str(self.stm32cube_serie_path),
190        )
191        if self.stm32cube_serie_path.exists():
192            # if already exists, then just clean and fetch
193            self.os_cmd(("git", "clean", "-fdx"), cwd=self.stm32cube_serie_path)
194            self.os_cmd(("git", "fetch"), cwd=self.stm32cube_serie_path)
195            # this is useful when HAL/CMSIS Device drivers submodules are not
196            # already present locally, otherwise "git fetch" is sufficient
197            self.os_cmd(("git", "submodule", "update", "--init"),
198                        cwd=self.stm32cube_serie_path)
199            branch = self.major_branch()
200            # "Using --recurse-submodules will update the content of all active
201            # submodules according to the commit recorded in the superproject.
202            # If local modifications in a submodule would be overwritten the
203            # checkout will fail unless -f is used."
204            # https://git-scm.com/docs/git-checkout
205            self.os_cmd(
206                ("git", "checkout", "-f", "--recurse-submodules", branch),
207                cwd=self.stm32cube_serie_path,
208            )
209        else:
210            # HAL & CMSIS Device drivers are now included as git submodules in
211            # upstream STM32Cube GitHub repositories
212            # So to get them too, --recursive or --recurse-submodules is needed
213            self.os_cmd(
214                ("git", "clone", "--recursive", repo_name),
215                cwd=self.stm32cube_repo_path,
216            )
217            branch = self.major_branch()
218
219        logging.info("%s", f"Branch used: {branch}")
220
221        # get the latest version of cube,
222        # with the most recent one created being the last entry.
223        self.os_cmd(("git", "checkout", branch), cwd=self.stm32cube_serie_path)
224        self.version_tag = subprocess.check_output(
225            ("git", "tag", "-l"), cwd=self.stm32cube_serie_path
226        ).splitlines()
227        self.version_tag = [x.decode("utf-8") for x in self.version_tag]
228        # Search latest version
229        if self.version_update == "":
230            self.version_update = self.version_tag[0]
231            for tag in self.version_tag:
232                if version_tuple(tag) > version_tuple(self.version_update):
233                    self.version_update = tag
234
235    def get_zephyr_current_version(self):
236        """Look for current zephyr hal version
237
238        Returns:
239            return previous zephyr cube version.
240
241        Raises:
242            ValueError: If version is not found.
243        """
244        with open(str(self.readme_file_path), "r") as f:
245            for line in f:
246                # pattern : "version " follow by optional "v",
247                # followed by x.y or x.y.z  x,y,z may represent several digits
248                # ex: 'version v1.8.9', 'version 10.20.25'
249                pattern = r".*version v?(\d+\.\d+\.?\d*).*$"
250                if re.match(pattern, line):
251                    previous_version = re.sub(pattern, r"\1", line).rstrip("\n")
252                    break
253
254        # Match previous version and list of existing tags
255        # which could be vx.y or x.y
256        pos_version = [
257            i for i, a in enumerate(self.version_tag) if previous_version in a
258        ]
259        if pos_version:
260            # return previous zephyr version
261            return self.version_tag[pos_version[0]]
262        else:
263            self.clean_files()
264            raise ValueError(
265                f"Error: cannot find version {previous_version} in STM32Cube_repo"
266            )
267
268    def extract_source(self):
269        """Extract sources and includes files from STM32Cube repo
270        and copy them in temporary directory
271        """
272        # for CMSIS files
273        temp_cmsis_soc_path = self.stm32cube_temp_serie / "soc"
274        Path.mkdir(temp_cmsis_soc_path, parents=True)
275
276        stm32cube_cmsis_include_path = (
277            self.stm32cube_serie_path
278            / "Drivers"
279            / "CMSIS"
280            / "Device"
281            / "ST"
282            / self.stm32_seriexx_upper
283            / "Include"
284        )
285        shutil.rmtree(temp_cmsis_soc_path, onerror=common_utils.remove_readonly)
286        shutil.copytree(stm32cube_cmsis_include_path, temp_cmsis_soc_path)
287
288        stm32cube_cmsis_templates_path = (
289            self.stm32cube_serie_path
290            / "Drivers"
291            / "CMSIS"
292            / "Device"
293            / "ST"
294            / self.stm32_seriexx_upper
295            / "Source"
296            / "Templates"
297        )
298        for repo_file in stm32cube_cmsis_templates_path.iterdir():
299            repo_src = stm32cube_cmsis_templates_path / repo_file
300            if repo_src.is_file():
301                shutil.copy(str(repo_src), str(temp_cmsis_soc_path))
302
303        # for hal and ll drivers
304        temp_drivers_include_path = self.stm32cube_temp_serie / "drivers" / "include"
305        temp_drivers_include_path.parent.mkdir(parents=True)
306
307        stm32cube_driver_inc = (
308            self.stm32cube_serie_path
309            / "Drivers"
310            / Path(self.stm32_seriexx_upper + "_HAL_Driver")
311            / "Inc"
312        )
313        if temp_drivers_include_path.exists():
314            shutil.rmtree(
315                temp_drivers_include_path, onerror=common_utils.remove_readonly
316            )
317        shutil.copytree(stm32cube_driver_inc, temp_drivers_include_path)
318
319        # except for _hal_conf_template.h which is renamed
320        self.rename_conf_template(temp_drivers_include_path)
321
322        temp_drivers_src_path = self.stm32cube_temp_serie / "drivers" / "src"
323        temp_drivers_src_path.mkdir()
324        stm32cube_drivers_src_path = (
325            self.stm32cube_serie_path
326            / "Drivers"
327            / Path(self.stm32_seriexx_upper + "_HAL_Driver")
328            / "Src"
329        )
330        shutil.rmtree(temp_drivers_src_path, onerror=common_utils.remove_readonly)
331        shutil.copytree(stm32cube_drivers_src_path, temp_drivers_src_path)
332
333    def build_from_current_cube_version(self):
334        """Build a commit in temporary dir with STM32Cube version
335        corresponding to zephyr current hal version
336        """
337        # Checkout the current Zephyr version of the STM32Cube repo
338        self.os_cmd(
339            ("git", "checkout", "-f", "--recurse-submodules", self.current_version),
340            cwd=self.stm32cube_serie_path,
341        )
342
343        # build the zephyr module from the stm32cube
344        self.extract_source()
345        logging.info(
346            "%s", "Building module from STM32Cube_repo " + self.current_version
347        )
348
349        if not self.stm32cube_temp_serie.parent.exists():
350            self.stm32cube_temp_serie.parent.mkdir(parents=True)
351
352        self.os_cmd(
353            ("git", "add", "-A", "stm32cube/" + self.stm32_seriexx + "/*"),
354            cwd=self.stm32cube_temp,
355        )
356        self.os_cmd(
357            ("git", "commit", "-am", '"module' + self.current_version + '"'),
358            cwd=self.stm32cube_temp,
359        )
360        # Remove trailing whitespaces
361        self.os_cmd(
362            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
363            cwd=self.stm32cube_temp,
364        )
365
366    def build_patch_from_current_zephyr_version(self):
367        """Build patch between zephyr current hal version and
368        corresponding official STM32Cube version
369        """
370        # clean-up the module
371        shutil.rmtree(
372            str(self.stm32cube_temp_serie), onerror=common_utils.remove_readonly
373        )
374
375        # populate the new repo with this current zephyr module
376        shutil.copytree(self.zephyr_module_serie_path, self.stm32cube_temp_serie)
377
378        # commit this current version module
379        self.os_cmd(("git", "add", "*"), cwd=self.stm32cube_temp)
380        self.os_cmd(("git", "commit", "-am", '"module"'), cwd=self.stm32cube_temp)
381
382        # Remove trailing space
383        self.os_cmd(
384            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
385            cwd=self.stm32cube_temp,
386        )
387
388        # generate a patch for files and _hal.conf.h file in the module
389        logging.info(
390            "%s",
391            "Building patch from official "
392            + self.current_version
393            + " to current zephyr module",
394        )
395
396        # For unclear reason, using tuple ("git", "diff", ...) is failing on Linux
397        # especially for this command. Keep a single string.
398        self.os_cmd(
399            ("git diff --ignore-space-at-eol HEAD~1 --output=" + self.module_patch),
400            shell=True,
401            cwd=self.stm32cube_temp,
402        )
403        self.os_cmd(("dos2unix", self.module_patch), cwd=self.stm32cube_temp)
404
405    def update_readme(self, make_version, make_commit):
406        """Update README file
407
408        Args:
409            make_version: latest STM32Cube version.
410            make_commit: Commit corresponding to latest STM32Cube version.
411        """
412        see_release_note = True
413
414        readme_path = self.zephyr_module_serie_path / "README"
415
416        with readme_path.open(mode="r") as readme_prev:
417            lines = (x for x in readme_prev.read().splitlines())
418
419        readme_path.unlink()
420
421        # Write README from previous one if exists
422        with open(str(readme_path), "w") as readme_file:
423            for LineItem in lines:
424                # change version nb
425                if "status" in LineItem.lower():
426                    readme_file.write("Status:\n")
427                    readme_file.write(f"   version {make_version}\n")
428                    next(lines)  # skip next line
429                elif "commit" in LineItem.lower():
430                    readme_file.write("Commit:\n")
431                    readme_file.write(f"   {make_commit}")
432                    next(lines)  # skip next line
433                elif "URL" in LineItem.upper():
434                    readme_file.write("URL:\n")
435                    readme_file.write(
436                        "   https://github.com/STMicroelectronics/"
437                        + f"STM32Cube{self.serie}\n"
438                    )
439                    next(lines)  # skip next line
440                # change patch list with a link to the release_note.html
441                elif "Patch List" in LineItem:
442                    readme_file.write("Patch List:\n")
443                    readme_file.write(
444                        "--> please check that the following list "
445                        + "is still valid:\n"
446                    )
447                else:
448                    if "See release_note.html from STM32Cube" in LineItem:
449                        see_release_note = False
450                    readme_file.write(f"{LineItem}\n")
451
452            # at the very end of the file :
453            if see_release_note:
454                readme_file.write("\n   See release_note.html from STM32Cube\n")
455            readme_file.flush()
456
457        self.os_cmd(("dos2unix", str(readme_path)))
458
459    def update_cmakelist(self):
460        """Update CMakeLists.txt file"""
461        cmakelists_path = self.zephyr_module_serie_path / "CMakeLists.txt"
462        if cmakelists_path.exists():
463            # build new CMakeLists.txt
464            with cmakelists_path.open("r") as cmakelists_old:
465                # this line is the copyright line
466                first_line = cmakelists_old.readline()
467                if "STMicroelectronics" in first_line:
468                    first_line = ""
469            cmakelists_path.unlink()
470        else:
471            first_line = ""
472
473        logging.info("%s", "Create a new CMakeLists.txt file")
474
475        with cmakelists_path.open("w") as cmakelists_new:
476            if first_line:
477                cmakelists_new.write(first_line)
478            cmakelists_new.write("# Copyright (c) 2020 STMicroelectronics\n")
479            cmakelists_new.write("#\n")
480            cmakelists_new.write("# SPDX-License-Identifier: Apache-2.0\n")
481            cmakelists_new.write("\n")
482
483            src_path = self.stm32cube_temp_serie / "drivers" / "src"
484            source_files = list(src_path.iterdir())
485            source_files.sort()
486
487            cmakelists_new.write(
488                f"zephyr_library_sources(soc/system_{self.stm32_seriexx}.c)\n"
489            )
490            cmakelists_new.write(
491                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal.c)\n"
492            )
493            cmakelists_new.write(
494                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal_rcc.c)\n"
495            )
496            cmakelists_new.write(
497                "zephyr_library_sources(drivers/src/"
498                + f"{self.stm32_seriexx}_hal_rcc_ex.c)\n"
499            )
500
501            for filename in source_files:
502                # also skipping  '_xxx_hal.c'
503                if "template" in filename.name:
504                    continue
505
506                # also skipping  '_xxx_hal_rcc.c' and '_xxx_hal_rcc_ex.c'
507                if "_hal_rcc" in filename.name:
508                    continue
509
510                if "_hal_" in filename.name:
511                    # retrieve IP name from filename, like adc,
512                    # which is delimited by
513                    #   * _hal_ on one side
514                    #   * and file extension on the other side
515                    pattern = r".*_hal_(.*)\..*"
516                    cmakelists_new.write(
517                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_HAL_"
518                        + re.sub(pattern, r"\1", filename.name).upper()
519                        + " drivers/src/"
520                        + filename.name
521                        + ")\n"
522                    )
523                if "_ll_" in filename.name:
524                    # retrieve IP name from filename, like adc,
525                    # which is delimited by
526                    #   * _ll_ on one side
527                    #   * and file extension on the other side
528                    pattern = r".*_ll_(.*)\..*"
529                    cmakelists_new.write(
530                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_LL_"
531                        + re.sub(pattern, r"\1", filename.name).upper()
532                        + " drivers/src/"
533                        + filename.name
534                        + ")\n"
535                    )
536        self.os_cmd(("dos2unix", str(cmakelists_path)))
537
538    def generate_assert_file(self):
539        """Remove stm32_assert_template.h file and create stm32_assert.h file"""
540        # remove stm32_assert_template.h
541        stm32_assert_template = (
542            self.stm32cube_temp_serie
543            / "drivers"
544            / "include"
545            / "stm32_assert_template.h"
546        )
547        if stm32_assert_template.exists():
548            stm32_assert_template.unlink()
549
550        # create stm32_assert.h from Jinja2 template
551        # Create the jinja2 environment.
552        templates_dir = self.zephyr_hal_stm32_path / "scripts"
553        j2_env = Environment(
554            loader=FileSystemLoader(str(templates_dir)),
555            trim_blocks=True,
556            lstrip_blocks=True,
557        )
558        stm32_assert_j2_template = j2_env.get_template("stm32_assert_template.txt")
559        stm32_assert_h = (
560            self.stm32cube_temp_serie / "drivers" / "include" / "stm32_assert.h"
561        )
562
563        with open(stm32_assert_h, "w") as stm32_assert_file:
564            stm32_assert_file.write(
565                stm32_assert_j2_template.render(stm32serie=self.stm32_serie)
566            )
567
568    def build_from_version_update(self):
569        """Build a commit in temporary dir with STM32Cube version
570        corresponding to zephyr latest hal version
571        """
572        # Checkout the latest version of the upstream STM32Cube repo
573        self.os_cmd(
574            ("git", "checkout", "-f", "--recurse-submodules", self.version_update),
575            cwd=self.stm32cube_serie_path,
576        )
577
578        # Get the commit id of this latest version
579        self.update_commit = subprocess.check_output(
580            ("git", "rev-parse", "HEAD"), cwd=self.stm32cube_serie_path
581        ).decode("utf-8")
582
583        # clear previous version content before populating with latest version
584        shutil.rmtree(
585            str(self.stm32cube_temp_serie), onerror=common_utils.remove_readonly
586        )
587
588        # populate temporary directory with latest version
589        self.extract_source()
590
591        # Commit files except log or patch files
592        self.os_cmd(("git", "add", "*"), cwd=self.stm32cube_serie_path)
593        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.stm32cube_serie_path)
594        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.stm32cube_serie_path)
595        self.os_cmd(
596            ("git", "commit", "-am", '"module' + self.version_update + '"'),
597            cwd=self.stm32cube_temp_serie,
598        )
599
600    def apply_zephyr_patch(self):
601        """Apply zephyr stm32 patch to latest stm32Cube version"""
602        logging.info("%s", "Apply zephyr patches to " + self.version_update)
603
604        # Copy from stm32cube_temp
605        shutil.rmtree(
606            str(self.zephyr_module_serie_path),
607            onerror=common_utils.remove_readonly,
608        )
609        shutil.copytree(self.stm32cube_temp_serie, self.zephyr_module_serie_path)
610
611        # apply dos2unix to whole zephyr hal serie sub directory
612        for child in self.zephyr_module_serie_path.glob("**/*"):
613            if child.is_file:
614                self.os_cmd(("dos2unix", child), cwd=self.zephyr_module_serie_path)
615
616        # Remove file that will be fully created by zephyr patch
617        # (otherwise applying patch will report error)
618        for file in zephyr_file_created:
619            if Path(self.zephyr_module_serie_path, file).exists():
620                Path(self.zephyr_module_serie_path, file).unlink()
621
622        # Apply patch from new repo
623        common_utils.apply_patch(
624            str(self.stm32cube_temp / self.module_patch), self.zephyr_hal_stm32_path
625        )
626
627        # save patch file so that it can be analysed in case of error
628        patch_path = self.zephyr_hal_stm32_path / self.module_patch
629        if patch_path.exists():
630            os.remove(patch_path)
631        shutil.copy(str(self.stm32cube_temp / self.module_patch), patch_path)
632
633        # Update README and CMakeList
634        self.update_readme(self.version_update, self.update_commit)
635        self.update_cmakelist()
636
637        # remove stm32_assert_template.h and create stm32_assert.h
638        self.generate_assert_file()
639
640        # Add files but do not commit
641        self.os_cmd(("git", "add", "*"), cwd=self.zephyr_hal_stm32_path)
642        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.zephyr_hal_stm32_path)
643        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.zephyr_hal_stm32_path)
644        self.os_cmd(("git", "reset", "--", "*.rej"), cwd=self.zephyr_hal_stm32_path)
645        logging.warning(
646            "%s",
647            "README file : --> please check that the Patch list is still valid",
648        )
649
650    def merge_commit(self, lib=False):
651        """Apply zephyr stm32 patch to latest stm32Cube version"""
652        # Merge & commit
653        # to clean the .rej files, uncomment line: reject()
654        # reject()
655        if lib:
656            logging.info("%s", "commit BLE library update")
657            commit_msg = "lib/stm32: "
658        else:
659            logging.info("%s", "commit HAL/LL Cube update ")
660            commit_msg = "stm32cube: "
661
662        commit_file_path = self.zephyr_module_serie_path / "commit.msg"
663        with open(commit_file_path, "w") as commit:
664            commit.write(
665                commit_msg
666                + "update "
667                + self.stm32_serie
668                + " to cube version "
669                + self.version_update.upper()
670                + "\n"
671            )
672
673            commit.write("\n")
674            commit.write(
675                "Update Cube version for " + self.stm32_seriexx_upper + " series" + "\n"
676            )
677            commit.write("on https://github.com/STMicroelectronics" + "\n")
678            commit.write(f"from version {self.current_version}\n")
679            commit.write(f"to version {self.version_update}\n")
680        self.os_cmd(
681            ("git", "commit", "-as", "-F", commit_file_path),
682            cwd=self.zephyr_module_serie_path,
683        )
684        self.os_cmd(
685            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
686            cwd=self.zephyr_module_serie_path,
687        )
688        Path(commit_file_path).unlink()
689
690    def reject(self):
691        """Clean *.rej files"""
692        reject_files = [f for f in self.zephyr_module_serie_path.glob("**/*.rej")]
693        if reject_files:
694            for f in reject_files:
695                f.unlink()
696
697    def cleanup_stm32cube_repo(self):
698        """clean the STM32Cube repo"""
699        self.os_cmd(("git", "checkout", "-f", "--recurse-submodules", "HEAD"),
700                    cwd=self.stm32cube_serie_path)
701
702    def clean_files(self):
703        """Clean repo file if required"""
704        # Remove temporary files unconditionally
705        os.chdir(os.getenv("HOME"))
706        shutil.rmtree(str(self.stm32cube_temp), onerror=common_utils.remove_readonly)
707
708        # remove STM32Cube repo only if required
709        if not self.noclean:
710            self.cleanup_stm32cube_repo()
711            shutil.rmtree(
712                str(self.stm32cube_repo_path), onerror=common_utils.remove_readonly
713            )
714        else:
715            self.os_cmd(
716                ("git", "checkout", "-f", "--recurse-submodules", "HEAD"),
717                cwd=self.stm32cube_serie_path,
718            )
719
720    def update_stm32_hal_serie(self):
721        """Update one single STM32Cube"""
722        # 1) clone full repo from github : get the repo STM32CubeXX
723        self.clone_cube_repo()
724
725        # 2) prepare a repo where to store module versions
726        self.os_cmd(("git", "init"), cwd=self.stm32cube_temp)
727        self.os_cmd(
728            ("git", "commit", "--allow-empty", "-m", "'Trigger notification'"),
729            cwd=self.stm32cube_temp,
730        )
731
732        # 3) get the version of cube which is in the zephyr module
733        self.current_version = self.get_zephyr_current_version()
734        logging.info(
735            f"Version {self.current_version} is the current zephyr version for "
736            + self.zephyr_module_serie_path.name
737        )
738
739        # do not process if versions are similar
740        if (self.current_version in self.version_update) or (
741            self.version_update in self.current_version
742        ):
743            logging.warning(
744                "%s",
745                f"*** Update abandoned: identical versions {self.version_update} ***\n",
746            )
747            self.clean_files()
748            return
749        logging.info(
750            f"*** Updating zephyr {self.stm32_serie} "
751            + f"from version {self.current_version} to {self.version_update}"
752        )
753
754        # 4) build the module from this previous version
755        self.build_from_current_cube_version()
756
757        # 5) build the patch from the current zephyr version
758        self.build_patch_from_current_zephyr_version()
759
760        # 6) build the module from this latest version
761        self.build_from_version_update()
762
763        # 7) apply zephyr patch : in the zephyr module repo
764        self.apply_zephyr_patch()
765        self.merge_commit()
766
767        # 8) In case of stm32wb, update ble library
768        if self.stm32_serie == "stm32wb":
769            ble_library.update(
770                self.stm32cube_serie_path,
771                Path(self.zephyr_hal_stm32_path / "lib" / "stm32wb"),
772                self.stm32cube_temp,
773                self.current_version,
774                self.version_update,
775                self.update_commit,
776                self.stm32_serie
777            )
778            self.merge_commit(lib=True)
779
780        # 9) In case of stm32wba, update hci library
781        elif self.stm32_serie == "stm32wba":
782            ble_library.update(
783                self.stm32cube_serie_path,
784                Path(self.zephyr_hal_stm32_path / "lib" / "stm32wba"),
785                self.stm32cube_temp,
786                self.current_version,
787                self.version_update,
788                self.update_commit,
789                self.stm32_serie
790            )
791            self.merge_commit(lib=True)
792
793        # 10) clean
794        self.clean_files()
795        logging.info("%s", f"Done {self.stm32_serie}\n")
796