1812c4071SJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 2812c4071SJuraj Linkeš# Copyright(c) 2010-2014 Intel Corporation 3680d8a24SJuraj Linkeš# Copyright(c) 2022-2023 PANTHEON.tech s.r.o. 4680d8a24SJuraj Linkeš# Copyright(c) 2022-2023 University of New Hampshire 5812c4071SJuraj Linkeš 6c9d31a7eSJuraj Linkešimport atexit 7c9d31a7eSJuraj Linkešimport os 8c9d31a7eSJuraj Linkešimport subprocess 957c58bf8SJuraj Linkešimport sys 10c9d31a7eSJuraj Linkešfrom enum import Enum 11c9d31a7eSJuraj Linkešfrom pathlib import Path 12c9d31a7eSJuraj Linkešfrom subprocess import SubprocessError 13c9d31a7eSJuraj Linkeš 14c9d31a7eSJuraj Linkešfrom .exception import ConfigurationError 15c9d31a7eSJuraj Linkeš 16c9d31a7eSJuraj Linkeš 17c9d31a7eSJuraj Linkešclass StrEnum(Enum): 18c9d31a7eSJuraj Linkeš @staticmethod 19c9d31a7eSJuraj Linkeš def _generate_next_value_( 20c9d31a7eSJuraj Linkeš name: str, start: int, count: int, last_values: object 21c9d31a7eSJuraj Linkeš ) -> str: 22c9d31a7eSJuraj Linkeš return name 23c9d31a7eSJuraj Linkeš 24c9d31a7eSJuraj Linkeš def __str__(self) -> str: 25c9d31a7eSJuraj Linkeš return self.name 2657c58bf8SJuraj Linkeš 2757c58bf8SJuraj Linkeš 28*88489c05SJeremy SpewockREGEX_FOR_PCI_ADDRESS = "/[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.[0-9]{1}/" 29*88489c05SJeremy Spewock 30*88489c05SJeremy Spewock 3157c58bf8SJuraj Linkešdef check_dts_python_version() -> None: 3257c58bf8SJuraj Linkeš if sys.version_info.major < 3 or ( 3357c58bf8SJuraj Linkeš sys.version_info.major == 3 and sys.version_info.minor < 10 3457c58bf8SJuraj Linkeš ): 3557c58bf8SJuraj Linkeš print( 3657c58bf8SJuraj Linkeš RED( 3757c58bf8SJuraj Linkeš ( 3857c58bf8SJuraj Linkeš "WARNING: DTS execution node's python version is lower than" 3957c58bf8SJuraj Linkeš "python 3.10, is deprecated and will not work in future releases." 4057c58bf8SJuraj Linkeš ) 4157c58bf8SJuraj Linkeš ), 4257c58bf8SJuraj Linkeš file=sys.stderr, 4357c58bf8SJuraj Linkeš ) 4457c58bf8SJuraj Linkeš print(RED("Please use Python >= 3.10 instead"), file=sys.stderr) 4557c58bf8SJuraj Linkeš 46812c4071SJuraj Linkeš 47c020b7ceSJuraj Linkešdef expand_range(range_str: str) -> list[int]: 48c020b7ceSJuraj Linkeš """ 49c020b7ceSJuraj Linkeš Process range string into a list of integers. There are two possible formats: 50c020b7ceSJuraj Linkeš n - a single integer 51c020b7ceSJuraj Linkeš n-m - a range of integers 52c020b7ceSJuraj Linkeš 53c020b7ceSJuraj Linkeš The returned range includes both n and m. Empty string returns an empty list. 54c020b7ceSJuraj Linkeš """ 55c020b7ceSJuraj Linkeš expanded_range: list[int] = [] 56c020b7ceSJuraj Linkeš if range_str: 57c020b7ceSJuraj Linkeš range_boundaries = range_str.split("-") 58c020b7ceSJuraj Linkeš # will throw an exception when items in range_boundaries can't be converted, 59c020b7ceSJuraj Linkeš # serving as type check 60c020b7ceSJuraj Linkeš expanded_range.extend( 61c020b7ceSJuraj Linkeš range(int(range_boundaries[0]), int(range_boundaries[-1]) + 1) 62c020b7ceSJuraj Linkeš ) 63c020b7ceSJuraj Linkeš 64c020b7ceSJuraj Linkeš return expanded_range 65c020b7ceSJuraj Linkeš 66c020b7ceSJuraj Linkeš 67812c4071SJuraj Linkešdef RED(text: str) -> str: 68812c4071SJuraj Linkeš return f"\u001B[31;1m{str(text)}\u001B[0m" 69680d8a24SJuraj Linkeš 70680d8a24SJuraj Linkeš 71680d8a24SJuraj Linkešclass MesonArgs(object): 72680d8a24SJuraj Linkeš """ 73680d8a24SJuraj Linkeš Aggregate the arguments needed to build DPDK: 74680d8a24SJuraj Linkeš default_library: Default library type, Meson allows "shared", "static" and "both". 75680d8a24SJuraj Linkeš Defaults to None, in which case the argument won't be used. 76680d8a24SJuraj Linkeš Keyword arguments: The arguments found in meson_options.txt in root DPDK directory. 77680d8a24SJuraj Linkeš Do not use -D with them, for example: 78680d8a24SJuraj Linkeš meson_args = MesonArgs(enable_kmods=True). 79680d8a24SJuraj Linkeš """ 80680d8a24SJuraj Linkeš 81680d8a24SJuraj Linkeš _default_library: str 82680d8a24SJuraj Linkeš 83680d8a24SJuraj Linkeš def __init__(self, default_library: str | None = None, **dpdk_args: str | bool): 84680d8a24SJuraj Linkeš self._default_library = ( 85680d8a24SJuraj Linkeš f"--default-library={default_library}" if default_library else "" 86680d8a24SJuraj Linkeš ) 87680d8a24SJuraj Linkeš self._dpdk_args = " ".join( 88680d8a24SJuraj Linkeš ( 89680d8a24SJuraj Linkeš f"-D{dpdk_arg_name}={dpdk_arg_value}" 90680d8a24SJuraj Linkeš for dpdk_arg_name, dpdk_arg_value in dpdk_args.items() 91680d8a24SJuraj Linkeš ) 92680d8a24SJuraj Linkeš ) 93680d8a24SJuraj Linkeš 94680d8a24SJuraj Linkeš def __str__(self) -> str: 95680d8a24SJuraj Linkeš return " ".join(f"{self._default_library} {self._dpdk_args}".split()) 96c9d31a7eSJuraj Linkeš 97c9d31a7eSJuraj Linkeš 98c9d31a7eSJuraj Linkešclass _TarCompressionFormat(StrEnum): 99c9d31a7eSJuraj Linkeš """Compression formats that tar can use. 100c9d31a7eSJuraj Linkeš 101c9d31a7eSJuraj Linkeš Enum names are the shell compression commands 102c9d31a7eSJuraj Linkeš and Enum values are the associated file extensions. 103c9d31a7eSJuraj Linkeš """ 104c9d31a7eSJuraj Linkeš 105c9d31a7eSJuraj Linkeš gzip = "gz" 106c9d31a7eSJuraj Linkeš compress = "Z" 107c9d31a7eSJuraj Linkeš bzip2 = "bz2" 108c9d31a7eSJuraj Linkeš lzip = "lz" 109c9d31a7eSJuraj Linkeš lzma = "lzma" 110c9d31a7eSJuraj Linkeš lzop = "lzo" 111c9d31a7eSJuraj Linkeš xz = "xz" 112c9d31a7eSJuraj Linkeš zstd = "zst" 113c9d31a7eSJuraj Linkeš 114c9d31a7eSJuraj Linkeš 115c9d31a7eSJuraj Linkešclass DPDKGitTarball(object): 116c9d31a7eSJuraj Linkeš """Create a compressed tarball of DPDK from the repository. 117c9d31a7eSJuraj Linkeš 118c9d31a7eSJuraj Linkeš The DPDK version is specified with git object git_ref. 119c9d31a7eSJuraj Linkeš The tarball will be compressed with _TarCompressionFormat, 120c9d31a7eSJuraj Linkeš which must be supported by the DTS execution environment. 121c9d31a7eSJuraj Linkeš The resulting tarball will be put into output_dir. 122c9d31a7eSJuraj Linkeš 123c9d31a7eSJuraj Linkeš The class supports the os.PathLike protocol, 124c9d31a7eSJuraj Linkeš which is used to get the Path of the tarball:: 125c9d31a7eSJuraj Linkeš 126c9d31a7eSJuraj Linkeš from pathlib import Path 127c9d31a7eSJuraj Linkeš tarball = DPDKGitTarball("HEAD", "output") 128c9d31a7eSJuraj Linkeš tarball_path = Path(tarball) 129c9d31a7eSJuraj Linkeš 130c9d31a7eSJuraj Linkeš Arguments: 131c9d31a7eSJuraj Linkeš git_ref: A git commit ID, tag ID or tree ID. 132c9d31a7eSJuraj Linkeš output_dir: The directory where to put the resulting tarball. 133c9d31a7eSJuraj Linkeš tar_compression_format: The compression format to use. 134c9d31a7eSJuraj Linkeš """ 135c9d31a7eSJuraj Linkeš 136c9d31a7eSJuraj Linkeš _git_ref: str 137c9d31a7eSJuraj Linkeš _tar_compression_format: _TarCompressionFormat 138c9d31a7eSJuraj Linkeš _tarball_dir: Path 139c9d31a7eSJuraj Linkeš _tarball_name: str 140c9d31a7eSJuraj Linkeš _tarball_path: Path | None 141c9d31a7eSJuraj Linkeš 142c9d31a7eSJuraj Linkeš def __init__( 143c9d31a7eSJuraj Linkeš self, 144c9d31a7eSJuraj Linkeš git_ref: str, 145c9d31a7eSJuraj Linkeš output_dir: str, 146c9d31a7eSJuraj Linkeš tar_compression_format: _TarCompressionFormat = _TarCompressionFormat.xz, 147c9d31a7eSJuraj Linkeš ): 148c9d31a7eSJuraj Linkeš self._git_ref = git_ref 149c9d31a7eSJuraj Linkeš self._tar_compression_format = tar_compression_format 150c9d31a7eSJuraj Linkeš 151c9d31a7eSJuraj Linkeš self._tarball_dir = Path(output_dir, "tarball") 152c9d31a7eSJuraj Linkeš 153c9d31a7eSJuraj Linkeš self._get_commit_id() 154c9d31a7eSJuraj Linkeš self._create_tarball_dir() 155c9d31a7eSJuraj Linkeš 156c9d31a7eSJuraj Linkeš self._tarball_name = ( 157c9d31a7eSJuraj Linkeš f"dpdk-tarball-{self._git_ref}.tar.{self._tar_compression_format.value}" 158c9d31a7eSJuraj Linkeš ) 159c9d31a7eSJuraj Linkeš self._tarball_path = self._check_tarball_path() 160c9d31a7eSJuraj Linkeš if not self._tarball_path: 161c9d31a7eSJuraj Linkeš self._create_tarball() 162c9d31a7eSJuraj Linkeš 163c9d31a7eSJuraj Linkeš def _get_commit_id(self) -> None: 164c9d31a7eSJuraj Linkeš result = subprocess.run( 165c9d31a7eSJuraj Linkeš ["git", "rev-parse", "--verify", self._git_ref], 166c9d31a7eSJuraj Linkeš text=True, 167c9d31a7eSJuraj Linkeš capture_output=True, 168c9d31a7eSJuraj Linkeš ) 169c9d31a7eSJuraj Linkeš if result.returncode != 0: 170c9d31a7eSJuraj Linkeš raise ConfigurationError( 171c9d31a7eSJuraj Linkeš f"{self._git_ref} is neither a path to an existing DPDK " 172c9d31a7eSJuraj Linkeš "archive nor a valid git reference.\n" 173c9d31a7eSJuraj Linkeš f"Command: {result.args}\n" 174c9d31a7eSJuraj Linkeš f"Stdout: {result.stdout}\n" 175c9d31a7eSJuraj Linkeš f"Stderr: {result.stderr}" 176c9d31a7eSJuraj Linkeš ) 177c9d31a7eSJuraj Linkeš self._git_ref = result.stdout.strip() 178c9d31a7eSJuraj Linkeš 179c9d31a7eSJuraj Linkeš def _create_tarball_dir(self) -> None: 180c9d31a7eSJuraj Linkeš os.makedirs(self._tarball_dir, exist_ok=True) 181c9d31a7eSJuraj Linkeš 182c9d31a7eSJuraj Linkeš def _check_tarball_path(self) -> Path | None: 183c9d31a7eSJuraj Linkeš if self._tarball_name in os.listdir(self._tarball_dir): 184c9d31a7eSJuraj Linkeš return Path(self._tarball_dir, self._tarball_name) 185c9d31a7eSJuraj Linkeš return None 186c9d31a7eSJuraj Linkeš 187c9d31a7eSJuraj Linkeš def _create_tarball(self) -> None: 188c9d31a7eSJuraj Linkeš self._tarball_path = Path(self._tarball_dir, self._tarball_name) 189c9d31a7eSJuraj Linkeš 190c9d31a7eSJuraj Linkeš atexit.register(self._delete_tarball) 191c9d31a7eSJuraj Linkeš 192c9d31a7eSJuraj Linkeš result = subprocess.run( 193c9d31a7eSJuraj Linkeš 'git -C "$(git rev-parse --show-toplevel)" archive ' 194c9d31a7eSJuraj Linkeš f'{self._git_ref} --prefix="dpdk-tarball-{self._git_ref + os.sep}" | ' 195c9d31a7eSJuraj Linkeš f"{self._tar_compression_format} > {Path(self._tarball_path.absolute())}", 196c9d31a7eSJuraj Linkeš shell=True, 197c9d31a7eSJuraj Linkeš text=True, 198c9d31a7eSJuraj Linkeš capture_output=True, 199c9d31a7eSJuraj Linkeš ) 200c9d31a7eSJuraj Linkeš 201c9d31a7eSJuraj Linkeš if result.returncode != 0: 202c9d31a7eSJuraj Linkeš raise SubprocessError( 203c9d31a7eSJuraj Linkeš f"Git archive creation failed with exit code {result.returncode}.\n" 204c9d31a7eSJuraj Linkeš f"Command: {result.args}\n" 205c9d31a7eSJuraj Linkeš f"Stdout: {result.stdout}\n" 206c9d31a7eSJuraj Linkeš f"Stderr: {result.stderr}" 207c9d31a7eSJuraj Linkeš ) 208c9d31a7eSJuraj Linkeš 209c9d31a7eSJuraj Linkeš atexit.unregister(self._delete_tarball) 210c9d31a7eSJuraj Linkeš 211c9d31a7eSJuraj Linkeš def _delete_tarball(self) -> None: 212c9d31a7eSJuraj Linkeš if self._tarball_path and os.path.exists(self._tarball_path): 213c9d31a7eSJuraj Linkeš os.remove(self._tarball_path) 214c9d31a7eSJuraj Linkeš 215c9d31a7eSJuraj Linkeš def __fspath__(self): 216c9d31a7eSJuraj Linkeš return str(self._tarball_path) 217