1#!/usr/bin/python
2"""
3SPDX-License-Identifier: Apache-2.0
4Copyright (c) 2019 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            branch = self.major_branch()
196            self.os_cmd(
197                ("git", "reset", "--hard", branch),
198                cwd=self.stm32cube_serie_path,
199            )
200        else:
201            self.os_cmd(
202                ("git", "clone", repo_name),
203                cwd=self.stm32cube_repo_path,
204            )
205            branch = self.major_branch()
206
207        logging.info("%s", f"Branch used: {branch}")
208
209        # get the latest version of cube,
210        # with the most recent one created being the last entry.
211        self.os_cmd(("git", "checkout", branch), cwd=self.stm32cube_serie_path)
212        self.version_tag = subprocess.check_output(
213            ("git", "tag", "-l"), cwd=self.stm32cube_serie_path
214        ).splitlines()
215        self.version_tag = [x.decode("utf-8") for x in self.version_tag]
216        # Search latest version
217        if self.version_update == "":
218            self.version_update = self.version_tag[0]
219            for tag in self.version_tag:
220                if version_tuple(tag) > version_tuple(self.version_update):
221                    self.version_update = tag
222
223    def get_zephyr_current_version(self):
224        """Look for current zephyr hal version
225
226        Returns:
227            return previous zephyr cube version.
228
229        Raises:
230            ValueError: If version is not found.
231        """
232        with open(str(self.readme_file_path), "r") as f:
233            for line in f:
234                # pattern : "version " follow by optional "v",
235                # followed by x.y or x.y.z  x,y,z may represent several digits
236                # ex: 'version v1.8.9', 'version 10.20.25'
237                pattern = r".*version v?(\d+\.\d+\.?\d*).*$"
238                if re.match(pattern, line):
239                    previous_version = re.sub(pattern, r"\1", line).rstrip("\n")
240                    break
241
242        # Match previous version and list of existing tags
243        # which could be vx.y or x.y
244        pos_version = [
245            i for i, a in enumerate(self.version_tag) if previous_version in a
246        ]
247        if pos_version:
248            # return previous zephyr version
249            return self.version_tag[pos_version[0]]
250        else:
251            self.clean_files()
252            raise ValueError(
253                f"Error: cannot find version {previous_version} in STM32Cube_repo"
254            )
255
256    def extract_source(self):
257        """Extract sources and includes files from STM32Cube repo
258        and copy them in temporary directory
259        """
260        # for CMSIS files
261        temp_cmsis_soc_path = self.stm32cube_temp_serie / "soc"
262        Path.mkdir(temp_cmsis_soc_path, parents=True)
263        stm32cube_cmsis_include_path = (
264            self.stm32cube_serie_path
265            / "Drivers"
266            / "CMSIS"
267            / "Device"
268            / "ST"
269            / self.stm32_seriexx_upper
270            / "Include"
271        )
272        shutil.rmtree(temp_cmsis_soc_path, onerror=common_utils.remove_readonly)
273        shutil.copytree(stm32cube_cmsis_include_path, temp_cmsis_soc_path)
274
275        stm32cube_cmsis_templates_path = (
276            self.stm32cube_serie_path
277            / "Drivers"
278            / "CMSIS"
279            / "Device"
280            / "ST"
281            / self.stm32_seriexx_upper
282            / "Source"
283            / "Templates"
284        )
285        for repo_file in stm32cube_cmsis_templates_path.iterdir():
286            repo_src = stm32cube_cmsis_templates_path / repo_file
287            if repo_src.is_file():
288                shutil.copy(str(repo_src), str(temp_cmsis_soc_path))
289
290        # for hal and ll drivers
291        temp_drivers_include_path = self.stm32cube_temp_serie / "drivers" / "include"
292        temp_drivers_include_path.parent.mkdir(parents=True)
293        stm32cube_driver_inc = (
294            self.stm32cube_serie_path
295            / "Drivers"
296            / Path(self.stm32_seriexx_upper + "_HAL_Driver")
297            / "Inc"
298        )
299        if temp_drivers_include_path.exists():
300            shutil.rmtree(
301                temp_drivers_include_path, onerror=common_utils.remove_readonly
302            )
303        shutil.copytree(stm32cube_driver_inc, temp_drivers_include_path)
304
305        # except for _hal_conf_template.h which is renamed
306        self.rename_conf_template(temp_drivers_include_path)
307
308        temp_drivers_src_path = self.stm32cube_temp_serie / "drivers" / "src"
309        temp_drivers_src_path.mkdir()
310        stm32cube_drivers_src_path = (
311            self.stm32cube_serie_path
312            / "Drivers"
313            / Path(self.stm32_seriexx_upper + "_HAL_Driver")
314            / "Src"
315        )
316        shutil.rmtree(temp_drivers_src_path, onerror=common_utils.remove_readonly)
317        shutil.copytree(stm32cube_drivers_src_path, temp_drivers_src_path)
318
319    def build_from_current_cube_version(self):
320        """Build a commit in temporary dir with STM32Cube version
321        corresponding to zephyr current hal version
322        """
323        # reset the STM32Cube repo to this current version
324        self.os_cmd(
325            ("git", "reset", "--hard", self.current_version),
326            cwd=self.stm32cube_serie_path,
327        )
328
329        # build the zephyr module from the stm32cube
330        self.extract_source()
331        logging.info(
332            "%s", "Building module from STM32Cube_repo " + self.current_version
333        )
334
335        if not self.stm32cube_temp_serie.parent.exists():
336            self.stm32cube_temp_serie.parent.mkdir(parents=True)
337
338        self.os_cmd(
339            ("git", "add", "-A", "stm32cube/" + self.stm32_seriexx + "/*"),
340            cwd=self.stm32cube_temp,
341        )
342        self.os_cmd(
343            ("git", "commit", "-am", '"module' + self.current_version + '"'),
344            cwd=self.stm32cube_temp,
345        )
346        # Remove trailing whitespaces
347        self.os_cmd(
348            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
349            cwd=self.stm32cube_temp,
350        )
351
352    def build_patch_from_current_zephyr_version(self):
353        """Build patch between zephyr current hal version and
354        corresponding official STM32Cube version
355        """
356        # clean-up the module
357        shutil.rmtree(
358            str(self.stm32cube_temp_serie), onerror=common_utils.remove_readonly
359        )
360
361        # populate the new repo with this current zephyr module
362        shutil.copytree(self.zephyr_module_serie_path, self.stm32cube_temp_serie)
363
364        # commit this current version module
365        self.os_cmd(("git", "add", "*"), cwd=self.stm32cube_temp)
366        self.os_cmd(("git", "commit", "-am", '"module"'), cwd=self.stm32cube_temp)
367
368        # Remove trailing space
369        self.os_cmd(
370            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
371            cwd=self.stm32cube_temp,
372        )
373
374        # generate a patch for files and _hal.conf.h file in the module
375        logging.info(
376            "%s",
377            "Building patch from official "
378            + self.current_version
379            + " to current zephyr module",
380        )
381
382        # For unclear reason, using tuple ("git", "diff", ...) is failing on Linux
383        # especially for this command. Keep a single string.
384        self.os_cmd(
385            ("git diff --ignore-space-at-eol HEAD~1 --output=" + self.module_patch),
386            shell=True,
387            cwd=self.stm32cube_temp,
388        )
389        self.os_cmd(("dos2unix", self.module_patch), cwd=self.stm32cube_temp)
390
391    def update_readme(self, make_version, make_commit):
392        """Update README file
393
394        Args:
395            make_version: latest STM32Cube version.
396            make_commit: Commit corresponding to latest STM32Cube version.
397        """
398        see_release_note = True
399
400        readme_path = self.zephyr_module_serie_path / "README"
401
402        with readme_path.open(mode="r") as readme_prev:
403            lines = (x for x in readme_prev.read().splitlines())
404
405        readme_path.unlink()
406
407        # Write README from previous one if exists
408        with open(str(readme_path), "w") as readme_file:
409            for LineItem in lines:
410                # change version nb
411                if "status" in LineItem.lower():
412                    readme_file.write("Status:\n")
413                    readme_file.write(f"   version {make_version}\n")
414                    next(lines)  # skip next line
415                elif "commit" in LineItem.lower():
416                    readme_file.write("Commit:\n")
417                    readme_file.write(f"   {make_commit}")
418                    next(lines)  # skip next line
419                elif "URL" in LineItem.upper():
420                    readme_file.write("URL:\n")
421                    readme_file.write(
422                        "   https://github.com/STMicroelectronics/"
423                        + f"STM32Cube{self.serie}\n"
424                    )
425                    next(lines)  # skip next line
426                # change patch list with a link to the release_note.html
427                elif "Patch List" in LineItem:
428                    readme_file.write("Patch List:\n")
429                    readme_file.write(
430                        "--> please check that the following list "
431                        + "is still valid:\n"
432                    )
433                else:
434                    if "See release_note.html from STM32Cube" in LineItem:
435                        see_release_note = False
436                    readme_file.write(f"{LineItem}\n")
437
438            # at the very end of the file :
439            if see_release_note:
440                readme_file.write("\n   See release_note.html from STM32Cube\n")
441            readme_file.flush()
442
443        self.os_cmd(("dos2unix", str(readme_path)))
444
445    def copy_release_note(self):
446        """Copy release_note.html file from STM32Cube to zephyr"""
447        release_note_src = self.stm32cube_serie_path / "Release_Notes.html"
448        release_note_dst = self.zephyr_module_serie_path / "release_note.html"
449        if release_note_dst.exists():
450            release_note_dst.unlink()
451        if release_note_src.exists:
452            release_note_src.rename(release_note_dst)
453            self.os_cmd(("dos2unix", str(release_note_dst)))
454
455    def update_cmakelist(self):
456        """Update CMakeLists.txt file"""
457        cmakelists_path = self.zephyr_module_serie_path / "CMakeLists.txt"
458        if cmakelists_path.exists():
459            # build new CMakeLists.txt
460            with cmakelists_path.open("r") as cmakelists_old:
461                # this line is the copyright line
462                first_line = cmakelists_old.readline()
463                if "STMicroelectronics" in first_line:
464                    first_line = ""
465            cmakelists_path.unlink()
466        else:
467            first_line = ""
468
469        logging.info("%s", "Create a new CMakeLists.txt file")
470
471        with cmakelists_path.open("w") as cmakelists_new:
472            if first_line:
473                cmakelists_new.write(first_line)
474            cmakelists_new.write("# Copyright (c) 2020 STMicroelectronics\n")
475            cmakelists_new.write("#\n")
476            cmakelists_new.write("# SPDX-License-Identifier: Apache-2.0\n")
477            cmakelists_new.write("\n")
478
479            src_path = self.stm32cube_temp_serie / "drivers" / "src"
480            source_files = list(src_path.iterdir())
481            source_files.sort()
482
483            cmakelists_new.write(
484                f"zephyr_library_sources(soc/system_{self.stm32_seriexx}.c)\n"
485            )
486            cmakelists_new.write(
487                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal.c)\n"
488            )
489            cmakelists_new.write(
490                f"zephyr_library_sources(drivers/src/{self.stm32_seriexx}_hal_rcc.c)\n"
491            )
492            cmakelists_new.write(
493                "zephyr_library_sources(drivers/src/"
494                + f"{self.stm32_seriexx}_hal_rcc_ex.c)\n"
495            )
496
497            for filename in source_files:
498                # also skipping  '_xxx_hal.c'
499                if "template" in filename.name:
500                    continue
501
502                # also skipping  '_xxx_hal_rcc.c' and '_xxx_hal_rcc_ex.c'
503                if "_hal_rcc" in filename.name:
504                    continue
505
506                if "_hal_" in filename.name:
507                    # retrieve IP name from filename, like adc,
508                    # which is delimited by
509                    #   * _hal_ on one side
510                    #   * and file extension on the other side
511                    pattern = r".*_hal_(.*)\..*"
512                    cmakelists_new.write(
513                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_HAL_"
514                        + re.sub(pattern, r"\1", filename.name).upper()
515                        + " drivers/src/"
516                        + filename.name
517                        + ")\n"
518                    )
519                if "_ll_" in filename.name:
520                    # retrieve IP name from filename, like adc,
521                    # which is delimited by
522                    #   * _ll_ on one side
523                    #   * and file extension on the other side
524                    pattern = r".*_ll_(.*)\..*"
525                    cmakelists_new.write(
526                        "zephyr_library_sources_ifdef(CONFIG_USE_STM32_LL_"
527                        + re.sub(pattern, r"\1", filename.name).upper()
528                        + " drivers/src/"
529                        + filename.name
530                        + ")\n"
531                    )
532        self.os_cmd(("dos2unix", str(cmakelists_path)))
533
534    def generate_assert_file(self):
535        """Remove stm32_assert_template.h file and create stm32_assert.h file"""
536        # remove stm32_assert_template.h
537        stm32_assert_template = (
538            self.stm32cube_temp_serie
539            / "drivers"
540            / "include"
541            / "stm32_assert_template.h"
542        )
543        if stm32_assert_template.exists():
544            stm32_assert_template.unlink()
545
546        # create stm32_assert.h from Jinja2 template
547        # Create the jinja2 environment.
548        templates_dir = self.zephyr_hal_stm32_path / "scripts"
549        j2_env = Environment(
550            loader=FileSystemLoader(str(templates_dir)),
551            trim_blocks=True,
552            lstrip_blocks=True,
553        )
554        stm32_assert_j2_template = j2_env.get_template("stm32_assert_template.txt")
555        stm32_assert_h = (
556            self.stm32cube_temp_serie / "drivers" / "include" / "stm32_assert.h"
557        )
558
559        with open(stm32_assert_h, "w") as stm32_assert_file:
560            stm32_assert_file.write(
561                stm32_assert_j2_template.render(stm32serie=self.stm32_serie)
562            )
563
564    def build_from_version_update(self):
565        """Build a commit in temporary dir with STM32Cube version
566        corresponding to zephyr latest hal version
567        """
568        # reset the STM32Cube repo to this latest version
569        self.os_cmd(
570            ("git", "reset", "--hard", self.version_update),
571            cwd=self.stm32cube_serie_path,
572        )
573
574        # Get the commit id of this latest version
575        self.update_commit = subprocess.check_output(
576            ("git", "rev-parse", "HEAD"), cwd=self.stm32cube_serie_path
577        ).decode("utf-8")
578
579        # clear previous version content before populating with latest version
580        shutil.rmtree(
581            str(self.stm32cube_temp_serie), onerror=common_utils.remove_readonly
582        )
583
584        # populate temporary directory with latest version
585        self.extract_source()
586
587        # Commit files except log or patch files
588        self.os_cmd(("git", "add", "*"), cwd=self.stm32cube_serie_path)
589        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.stm32cube_serie_path)
590        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.stm32cube_serie_path)
591        self.os_cmd(
592            ("git", "commit", "-am", '"module' + self.version_update + '"'),
593            cwd=self.stm32cube_temp_serie,
594        )
595
596    def apply_zephyr_patch(self):
597        """Apply zephyr stm32 patch to latest stm32Cube version"""
598        logging.info("%s", "Apply zephyr patches to " + self.version_update)
599
600        # Copy from stm32cube_temp
601        shutil.rmtree(
602            str(self.zephyr_module_serie_path),
603            onerror=common_utils.remove_readonly,
604        )
605        shutil.copytree(self.stm32cube_temp_serie, self.zephyr_module_serie_path)
606
607        # apply dos2unix to whole zephyr hal serie sub directory
608        for child in self.zephyr_module_serie_path.glob("**/*"):
609            if child.is_file:
610                self.os_cmd(("dos2unix", child), cwd=self.zephyr_module_serie_path)
611
612        # Remove file that will be fully created by zephyr patch
613        # (otherwise applying patch will report error)
614        for file in zephyr_file_created:
615            if Path(self.zephyr_module_serie_path, file).exists():
616                Path(self.zephyr_module_serie_path, file).unlink()
617
618        # Apply patch from new repo
619        common_utils.apply_patch(
620            str(self.stm32cube_temp / self.module_patch), self.zephyr_hal_stm32_path
621        )
622
623        # save patch file so that it can be analysed in case of error
624        patch_path = self.zephyr_hal_stm32_path / self.module_patch
625        if patch_path.exists():
626            os.remove(patch_path)
627        shutil.copy(str(self.stm32cube_temp / self.module_patch), patch_path)
628
629        # Update README and CMakeList, copy release note
630        self.update_readme(self.version_update, self.update_commit)
631        self.update_cmakelist()
632        self.copy_release_note()
633
634        # remove stm32_assert_template.h and create stm32_assert.h
635        self.generate_assert_file()
636
637        # Add files but do not commit
638        self.os_cmd(("git", "add", "*"), cwd=self.zephyr_hal_stm32_path)
639        self.os_cmd(("git", "reset", "--", "*.patch"), cwd=self.zephyr_hal_stm32_path)
640        self.os_cmd(("git", "reset", "--", "*.log"), cwd=self.zephyr_hal_stm32_path)
641        self.os_cmd(("git", "reset", "--", "*.rej"), cwd=self.zephyr_hal_stm32_path)
642        logging.warning(
643            "%s",
644            "README file : --> please check that the Patch list is still valid",
645        )
646
647    def merge_commit(self, lib=False):
648        """Apply zephyr stm32 patch to latest stm32Cube version"""
649        # Merge & commit
650        # to clean the .rej files, uncomment line: reject()
651        # reject()
652        if lib:
653            logging.info("%s", "commit BLE library update")
654            commit_msg = "lib/stm32: "
655        else:
656            logging.info("%s", "commit HAL/LL Cube update ")
657            commit_msg = "stm32cube: "
658
659        commit_file_path = self.zephyr_module_serie_path / "commit.msg"
660        with open(commit_file_path, "w") as commit:
661            commit.write(
662                commit_msg
663                + "update "
664                + self.stm32_serie
665                + " to cube version "
666                + self.version_update.upper()
667                + "\n"
668            )
669
670            commit.write("\n")
671            commit.write(
672                "Update Cube version for " + self.stm32_seriexx_upper + " series" + "\n"
673            )
674            commit.write("on https://github.com/STMicroelectronics" + "\n")
675            commit.write(f"from version {self.current_version}\n")
676            commit.write(f"to version {self.version_update}\n")
677        self.os_cmd(
678            ("git", "commit", "-as", "-F", commit_file_path),
679            cwd=self.zephyr_module_serie_path,
680        )
681        self.os_cmd(
682            ("git", "rebase", "--whitespace=fix", "HEAD~1"),
683            cwd=self.zephyr_module_serie_path,
684        )
685        Path(commit_file_path).unlink()
686
687    def reject(self):
688        """Clean *.rej files"""
689        reject_files = [f for f in self.zephyr_module_serie_path.glob("**/*.rej")]
690        if reject_files:
691            for f in reject_files:
692                f.unlink()
693
694    def cleanup_stm32cube_repo(self):
695        """clean the STM32Cube repo"""
696        self.os_cmd(("git", "reset", "--hard", "HEAD"), cwd=self.stm32cube_serie_path)
697
698    def clean_files(self):
699        """Clean repo file if required"""
700        # Remove temporary files unconditionally
701        os.chdir(os.getenv("HOME"))
702        shutil.rmtree(str(self.stm32cube_temp), onerror=common_utils.remove_readonly)
703
704        # remove STM32Cube repo only if required
705        if not self.noclean:
706            self.cleanup_stm32cube_repo()
707            shutil.rmtree(
708                str(self.stm32cube_repo_path), onerror=common_utils.remove_readonly
709            )
710        else:
711            self.os_cmd(
712                ("git", "reset", "--hard", "HEAD"),
713                cwd=self.stm32cube_serie_path,
714            )
715
716    def update_stm32_hal_serie(self):
717        """Update one single STM32Cube"""
718        # 1) clone full repo from github : get the repo STM32CubeXX
719        self.clone_cube_repo()
720
721        # 2) prepare a repo where to store module versions
722        self.os_cmd(("git", "init"), cwd=self.stm32cube_temp)
723        self.os_cmd(
724            ("git", "commit", "--allow-empty", "-m", "'Trigger notification'"),
725            cwd=self.stm32cube_temp,
726        )
727
728        # 3) get the version of cube which is in the zephyr module
729        self.current_version = self.get_zephyr_current_version()
730        logging.info(
731            f"Version {self.current_version} is the current zephyr version for "
732            + self.zephyr_module_serie_path.name
733        )
734
735        # do not process if versions are similar
736        if (self.current_version in self.version_update) or (
737            self.version_update in self.current_version
738        ):
739            logging.warning(
740                "%s",
741                f"*** Update abandoned: identical versions {self.version_update} ***\n",
742            )
743            self.clean_files()
744            return
745        logging.info(
746            f"*** Updating zephyr {self.stm32_serie} "
747            + f"from version {self.current_version} to {self.version_update}"
748        )
749
750        # 4) build the module from this previous version
751        self.build_from_current_cube_version()
752
753        # 5) build the patch from the current zephyr version
754        self.build_patch_from_current_zephyr_version()
755
756        # 6) build the module from this latest version
757        self.build_from_version_update()
758
759        # 7) apply zephyr patch : in the zephyr module repo
760        self.apply_zephyr_patch()
761        self.merge_commit()
762
763        # 8) In case of stm32wb, update ble library
764        if self.stm32_serie == "stm32wb":
765            ble_library.update(
766                self.stm32cube_serie_path,
767                Path(self.zephyr_hal_stm32_path / "lib"),
768                self.stm32cube_temp,
769                self.current_version,
770                self.version_update,
771                self.update_commit,
772            )
773            self.merge_commit(lib=True)
774
775        # 9) clean
776        self.clean_files()
777        logging.info("%s", f"Done {self.stm32_serie}\n")
778