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 copy_release_note(self):
460        """Copy release_note.html file from STM32Cube to zephyr"""
461        release_note_src = self.stm32cube_serie_path / "Release_Notes.html"
462        release_note_dst = self.zephyr_module_serie_path / "release_note.html"
463        if release_note_dst.exists():
464            release_note_dst.unlink()
465        if release_note_src.exists:
466            release_note_src.rename(release_note_dst)
467            self.os_cmd(("dos2unix", str(release_note_dst)))
468
469    def update_cmakelist(self):
470        """Update CMakeLists.txt file"""
471        cmakelists_path = self.zephyr_module_serie_path / "CMakeLists.txt"
472        if cmakelists_path.exists():
473            # build new CMakeLists.txt
474            with cmakelists_path.open("r") as cmakelists_old:
475                # this line is the copyright line
476                first_line = cmakelists_old.readline()
477                if "STMicroelectronics" in first_line:
478                    first_line = ""
479            cmakelists_path.unlink()
480        else:
481            first_line = ""
482
483        logging.info("%s", "Create a new CMakeLists.txt file")
484
485        with cmakelists_path.open("w") as cmakelists_new:
486            if first_line:
487                cmakelists_new.write(first_line)
488            cmakelists_new.write("# Copyright (c) 2020 STMicroelectronics\n")
489            cmakelists_new.write("#\n")
490            cmakelists_new.write("# SPDX-License-Identifier: Apache-2.0\n")
491            cmakelists_new.write("\n")
492
493            src_path = self.stm32cube_temp_serie / "drivers" / "src"
494            source_files = list(src_path.iterdir())
495            source_files.sort()
496
497            cmakelists_new.write(
498                f"zephyr_library_sources(soc/system_{self.stm32_seriexx}.c)\n"
499            )
500            cmakelists_new.write(
501                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal.c)\n"
502            )
503            cmakelists_new.write(
504                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal_rcc.c)\n"
505            )
506            cmakelists_new.write(
507                "zephyr_library_sources(drivers/src/"
508                + f"{self.stm32_seriexx}_hal_rcc_ex.c)\n"
509            )
510
511            for filename in source_files:
512                # also skipping  '_xxx_hal.c'
513                if "template" in filename.name:
514                    continue
515
516                # also skipping  '_xxx_hal_rcc.c' and '_xxx_hal_rcc_ex.c'
517                if "_hal_rcc" in filename.name:
518                    continue
519
520                if "_hal_" in filename.name:
521                    # retrieve IP name from filename, like adc,
522                    # which is delimited by
523                    #   * _hal_ on one side
524                    #   * and file extension on the other side
525                    pattern = r".*_hal_(.*)\..*"
526                    cmakelists_new.write(
527                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_HAL_"
528                        + re.sub(pattern, r"\1", filename.name).upper()
529                        + " drivers/src/"
530                        + filename.name
531                        + ")\n"
532                    )
533                if "_ll_" in filename.name:
534                    # retrieve IP name from filename, like adc,
535                    # which is delimited by
536                    #   * _ll_ on one side
537                    #   * and file extension on the other side
538                    pattern = r".*_ll_(.*)\..*"
539                    cmakelists_new.write(
540                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_LL_"
541                        + re.sub(pattern, r"\1", filename.name).upper()
542                        + " drivers/src/"
543                        + filename.name
544                        + ")\n"
545                    )
546        self.os_cmd(("dos2unix", str(cmakelists_path)))
547
548    def generate_assert_file(self):
549        """Remove stm32_assert_template.h file and create stm32_assert.h file"""
550        # remove stm32_assert_template.h
551        stm32_assert_template = (
552            self.stm32cube_temp_serie
553            / "drivers"
554            / "include"
555            / "stm32_assert_template.h"
556        )
557        if stm32_assert_template.exists():
558            stm32_assert_template.unlink()
559
560        # create stm32_assert.h from Jinja2 template
561        # Create the jinja2 environment.
562        templates_dir = self.zephyr_hal_stm32_path / "scripts"
563        j2_env = Environment(
564            loader=FileSystemLoader(str(templates_dir)),
565            trim_blocks=True,
566            lstrip_blocks=True,
567        )
568        stm32_assert_j2_template = j2_env.get_template("stm32_assert_template.txt")
569        stm32_assert_h = (
570            self.stm32cube_temp_serie / "drivers" / "include" / "stm32_assert.h"
571        )
572
573        with open(stm32_assert_h, "w") as stm32_assert_file:
574            stm32_assert_file.write(
575                stm32_assert_j2_template.render(stm32serie=self.stm32_serie)
576            )
577
578    def build_from_version_update(self):
579        """Build a commit in temporary dir with STM32Cube version
580        corresponding to zephyr latest hal version
581        """
582        # Checkout the latest version of the upstream STM32Cube repo
583        self.os_cmd(
584            ("git", "checkout", "-f", "--recurse-submodules", self.version_update),
585            cwd=self.stm32cube_serie_path,
586        )
587
588        # Get the commit id of this latest version
589        self.update_commit = subprocess.check_output(
590            ("git", "rev-parse", "HEAD"), cwd=self.stm32cube_serie_path
591        ).decode("utf-8")
592
593        # clear previous version content before populating with latest version
594        shutil.rmtree(
595            str(self.stm32cube_temp_serie), onerror=common_utils.remove_readonly
596        )
597
598        # populate temporary directory with latest version
599        self.extract_source()
600
601        # Commit files except log or patch files
602        self.os_cmd(("git", "add", "*"), cwd=self.stm32cube_serie_path)
603        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.stm32cube_serie_path)
604        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.stm32cube_serie_path)
605        self.os_cmd(
606            ("git", "commit", "-am", '"module' + self.version_update + '"'),
607            cwd=self.stm32cube_temp_serie,
608        )
609
610    def apply_zephyr_patch(self):
611        """Apply zephyr stm32 patch to latest stm32Cube version"""
612        logging.info("%s", "Apply zephyr patches to " + self.version_update)
613
614        # Copy from stm32cube_temp
615        shutil.rmtree(
616            str(self.zephyr_module_serie_path),
617            onerror=common_utils.remove_readonly,
618        )
619        shutil.copytree(self.stm32cube_temp_serie, self.zephyr_module_serie_path)
620
621        # apply dos2unix to whole zephyr hal serie sub directory
622        for child in self.zephyr_module_serie_path.glob("**/*"):
623            if child.is_file:
624                self.os_cmd(("dos2unix", child), cwd=self.zephyr_module_serie_path)
625
626        # Remove file that will be fully created by zephyr patch
627        # (otherwise applying patch will report error)
628        for file in zephyr_file_created:
629            if Path(self.zephyr_module_serie_path, file).exists():
630                Path(self.zephyr_module_serie_path, file).unlink()
631
632        # Apply patch from new repo
633        common_utils.apply_patch(
634            str(self.stm32cube_temp / self.module_patch), self.zephyr_hal_stm32_path
635        )
636
637        # save patch file so that it can be analysed in case of error
638        patch_path = self.zephyr_hal_stm32_path / self.module_patch
639        if patch_path.exists():
640            os.remove(patch_path)
641        shutil.copy(str(self.stm32cube_temp / self.module_patch), patch_path)
642
643        # Update README and CMakeList, copy release note
644        self.update_readme(self.version_update, self.update_commit)
645        self.update_cmakelist()
646        self.copy_release_note()
647
648        # remove stm32_assert_template.h and create stm32_assert.h
649        self.generate_assert_file()
650
651        # Add files but do not commit
652        self.os_cmd(("git", "add", "*"), cwd=self.zephyr_hal_stm32_path)
653        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.zephyr_hal_stm32_path)
654        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.zephyr_hal_stm32_path)
655        self.os_cmd(("git", "reset", "--", "*.rej"), cwd=self.zephyr_hal_stm32_path)
656        logging.warning(
657            "%s",
658            "README file : --> please check that the Patch list is still valid",
659        )
660
661    def merge_commit(self, lib=False):
662        """Apply zephyr stm32 patch to latest stm32Cube version"""
663        # Merge & commit
664        # to clean the .rej files, uncomment line: reject()
665        # reject()
666        if lib:
667            logging.info("%s", "commit BLE library update")
668            commit_msg = "lib/stm32: "
669        else:
670            logging.info("%s", "commit HAL/LL Cube update ")
671            commit_msg = "stm32cube: "
672
673        commit_file_path = self.zephyr_module_serie_path / "commit.msg"
674        with open(commit_file_path, "w") as commit:
675            commit.write(
676                commit_msg
677                + "update "
678                + self.stm32_serie
679                + " to cube version "
680                + self.version_update.upper()
681                + "\n"
682            )
683
684            commit.write("\n")
685            commit.write(
686                "Update Cube version for " + self.stm32_seriexx_upper + " series" + "\n"
687            )
688            commit.write("on https://github.com/STMicroelectronics" + "\n")
689            commit.write(f"from version {self.current_version}\n")
690            commit.write(f"to version {self.version_update}\n")
691        self.os_cmd(
692            ("git", "commit", "-as", "-F", commit_file_path),
693            cwd=self.zephyr_module_serie_path,
694        )
695        self.os_cmd(
696            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
697            cwd=self.zephyr_module_serie_path,
698        )
699        Path(commit_file_path).unlink()
700
701    def reject(self):
702        """Clean *.rej files"""
703        reject_files = [f for f in self.zephyr_module_serie_path.glob("**/*.rej")]
704        if reject_files:
705            for f in reject_files:
706                f.unlink()
707
708    def cleanup_stm32cube_repo(self):
709        """clean the STM32Cube repo"""
710        self.os_cmd(("git", "checkout", "-f", "--recurse-submodules", "HEAD"),
711                    cwd=self.stm32cube_serie_path)
712
713    def clean_files(self):
714        """Clean repo file if required"""
715        # Remove temporary files unconditionally
716        os.chdir(os.getenv("HOME"))
717        shutil.rmtree(str(self.stm32cube_temp), onerror=common_utils.remove_readonly)
718
719        # remove STM32Cube repo only if required
720        if not self.noclean:
721            self.cleanup_stm32cube_repo()
722            shutil.rmtree(
723                str(self.stm32cube_repo_path), onerror=common_utils.remove_readonly
724            )
725        else:
726            self.os_cmd(
727                ("git", "checkout", "-f", "--recurse-submodules", "HEAD"),
728                cwd=self.stm32cube_serie_path,
729            )
730
731    def update_stm32_hal_serie(self):
732        """Update one single STM32Cube"""
733        # 1) clone full repo from github : get the repo STM32CubeXX
734        self.clone_cube_repo()
735
736        # 2) prepare a repo where to store module versions
737        self.os_cmd(("git", "init"), cwd=self.stm32cube_temp)
738        self.os_cmd(
739            ("git", "commit", "--allow-empty", "-m", "'Trigger notification'"),
740            cwd=self.stm32cube_temp,
741        )
742
743        # 3) get the version of cube which is in the zephyr module
744        self.current_version = self.get_zephyr_current_version()
745        logging.info(
746            f"Version {self.current_version} is the current zephyr version for "
747            + self.zephyr_module_serie_path.name
748        )
749
750        # do not process if versions are similar
751        if (self.current_version in self.version_update) or (
752            self.version_update in self.current_version
753        ):
754            logging.warning(
755                "%s",
756                f"*** Update abandoned: identical versions {self.version_update} ***\n",
757            )
758            self.clean_files()
759            return
760        logging.info(
761            f"*** Updating zephyr {self.stm32_serie} "
762            + f"from version {self.current_version} to {self.version_update}"
763        )
764
765        # 4) build the module from this previous version
766        self.build_from_current_cube_version()
767
768        # 5) build the patch from the current zephyr version
769        self.build_patch_from_current_zephyr_version()
770
771        # 6) build the module from this latest version
772        self.build_from_version_update()
773
774        # 7) apply zephyr patch : in the zephyr module repo
775        self.apply_zephyr_patch()
776        self.merge_commit()
777
778        # 8) In case of stm32wb, update ble library
779        if self.stm32_serie == "stm32wb":
780            ble_library.update(
781                self.stm32cube_serie_path,
782                Path(self.zephyr_hal_stm32_path / "lib" / "stm32wb"),
783                self.stm32cube_temp,
784                self.current_version,
785                self.version_update,
786                self.update_commit,
787                self.stm32_serie
788            )
789            self.merge_commit(lib=True)
790
791        # 9) In case of stm32wba, update hci library
792        elif self.stm32_serie == "stm32wba":
793            ble_library.update(
794                self.stm32cube_serie_path,
795                Path(self.zephyr_hal_stm32_path / "lib" / "stm32wba"),
796                self.stm32cube_temp,
797                self.current_version,
798                self.version_update,
799                self.update_commit,
800                self.stm32_serie
801            )
802            self.merge_commit(lib=True)
803
804        # 10) clean
805        self.clean_files()
806        logging.info("%s", f"Done {self.stm32_serie}\n")
807