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