| Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 1 | # | 
|  | 2 | # SPDX-License-Identifier: GPL-2.0-only | 
|  | 3 | # | 
|  | 4 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 5 | from abc import ABCMeta, abstractmethod | 
|  | 6 | import os | 
|  | 7 | import re | 
|  | 8 | import bb | 
|  | 9 |  | 
|  | 10 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 11 | class Manifest(object, metaclass=ABCMeta): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 12 | """ | 
|  | 13 | This is an abstract class. Do not instantiate this directly. | 
|  | 14 | """ | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 15 |  | 
|  | 16 | PKG_TYPE_MUST_INSTALL = "mip" | 
|  | 17 | PKG_TYPE_MULTILIB = "mlp" | 
|  | 18 | PKG_TYPE_LANGUAGE = "lgp" | 
|  | 19 | PKG_TYPE_ATTEMPT_ONLY = "aop" | 
|  | 20 |  | 
|  | 21 | MANIFEST_TYPE_IMAGE = "image" | 
|  | 22 | MANIFEST_TYPE_SDK_HOST = "sdk_host" | 
|  | 23 | MANIFEST_TYPE_SDK_TARGET = "sdk_target" | 
|  | 24 |  | 
|  | 25 | var_maps = { | 
|  | 26 | MANIFEST_TYPE_IMAGE: { | 
|  | 27 | "PACKAGE_INSTALL": PKG_TYPE_MUST_INSTALL, | 
|  | 28 | "PACKAGE_INSTALL_ATTEMPTONLY": PKG_TYPE_ATTEMPT_ONLY, | 
|  | 29 | "LINGUAS_INSTALL": PKG_TYPE_LANGUAGE | 
|  | 30 | }, | 
|  | 31 | MANIFEST_TYPE_SDK_HOST: { | 
|  | 32 | "TOOLCHAIN_HOST_TASK": PKG_TYPE_MUST_INSTALL, | 
|  | 33 | "TOOLCHAIN_HOST_TASK_ATTEMPTONLY": PKG_TYPE_ATTEMPT_ONLY | 
|  | 34 | }, | 
|  | 35 | MANIFEST_TYPE_SDK_TARGET: { | 
|  | 36 | "TOOLCHAIN_TARGET_TASK": PKG_TYPE_MUST_INSTALL, | 
|  | 37 | "TOOLCHAIN_TARGET_TASK_ATTEMPTONLY": PKG_TYPE_ATTEMPT_ONLY | 
|  | 38 | } | 
|  | 39 | } | 
|  | 40 |  | 
|  | 41 | INSTALL_ORDER = [ | 
|  | 42 | PKG_TYPE_LANGUAGE, | 
|  | 43 | PKG_TYPE_MUST_INSTALL, | 
|  | 44 | PKG_TYPE_ATTEMPT_ONLY, | 
|  | 45 | PKG_TYPE_MULTILIB | 
|  | 46 | ] | 
|  | 47 |  | 
|  | 48 | initial_manifest_file_header = \ | 
|  | 49 | "# This file was generated automatically and contains the packages\n" \ | 
|  | 50 | "# passed on to the package manager in order to create the rootfs.\n\n" \ | 
|  | 51 | "# Format:\n" \ | 
|  | 52 | "#  <package_type>,<package_name>\n" \ | 
|  | 53 | "# where:\n" \ | 
|  | 54 | "#   <package_type> can be:\n" \ | 
|  | 55 | "#      'mip' = must install package\n" \ | 
|  | 56 | "#      'aop' = attempt only package\n" \ | 
|  | 57 | "#      'mlp' = multilib package\n" \ | 
|  | 58 | "#      'lgp' = language package\n\n" | 
|  | 59 |  | 
|  | 60 | def __init__(self, d, manifest_dir=None, manifest_type=MANIFEST_TYPE_IMAGE): | 
|  | 61 | self.d = d | 
|  | 62 | self.manifest_type = manifest_type | 
|  | 63 |  | 
|  | 64 | if manifest_dir is None: | 
|  | 65 | if manifest_type != self.MANIFEST_TYPE_IMAGE: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 66 | self.manifest_dir = self.d.getVar('SDK_DIR') | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 67 | else: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 68 | self.manifest_dir = self.d.getVar('WORKDIR') | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 69 | else: | 
|  | 70 | self.manifest_dir = manifest_dir | 
|  | 71 |  | 
|  | 72 | bb.utils.mkdirhier(self.manifest_dir) | 
|  | 73 |  | 
|  | 74 | self.initial_manifest = os.path.join(self.manifest_dir, "%s_initial_manifest" % manifest_type) | 
|  | 75 | self.final_manifest = os.path.join(self.manifest_dir, "%s_final_manifest" % manifest_type) | 
|  | 76 | self.full_manifest = os.path.join(self.manifest_dir, "%s_full_manifest" % manifest_type) | 
|  | 77 |  | 
|  | 78 | # packages in the following vars will be split in 'must install' and | 
|  | 79 | # 'multilib' | 
|  | 80 | self.vars_to_split = ["PACKAGE_INSTALL", | 
|  | 81 | "TOOLCHAIN_HOST_TASK", | 
|  | 82 | "TOOLCHAIN_TARGET_TASK"] | 
|  | 83 |  | 
|  | 84 | """ | 
|  | 85 | This creates a standard initial manifest for core-image-(minimal|sato|sato-sdk). | 
|  | 86 | This will be used for testing until the class is implemented properly! | 
|  | 87 | """ | 
|  | 88 | def _create_dummy_initial(self): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 89 | image_rootfs = self.d.getVar('IMAGE_ROOTFS') | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 90 | pkg_list = dict() | 
|  | 91 | if image_rootfs.find("core-image-sato-sdk") > 0: | 
|  | 92 | pkg_list[self.PKG_TYPE_MUST_INSTALL] = \ | 
|  | 93 | "packagegroup-core-x11-sato-games packagegroup-base-extended " \ | 
|  | 94 | "packagegroup-core-x11-sato packagegroup-core-x11-base " \ | 
|  | 95 | "packagegroup-core-sdk packagegroup-core-tools-debug " \ | 
|  | 96 | "packagegroup-core-boot packagegroup-core-tools-testapps " \ | 
|  | 97 | "packagegroup-core-eclipse-debug packagegroup-core-qt-demoapps " \ | 
|  | 98 | "apt packagegroup-core-tools-profile psplash " \ | 
|  | 99 | "packagegroup-core-standalone-sdk-target " \ | 
|  | 100 | "packagegroup-core-ssh-openssh dpkg kernel-dev" | 
|  | 101 | pkg_list[self.PKG_TYPE_LANGUAGE] = \ | 
|  | 102 | "locale-base-en-us locale-base-en-gb" | 
|  | 103 | elif image_rootfs.find("core-image-sato") > 0: | 
|  | 104 | pkg_list[self.PKG_TYPE_MUST_INSTALL] = \ | 
|  | 105 | "packagegroup-core-ssh-dropbear packagegroup-core-x11-sato-games " \ | 
|  | 106 | "packagegroup-core-x11-base psplash apt dpkg packagegroup-base-extended " \ | 
|  | 107 | "packagegroup-core-x11-sato packagegroup-core-boot" | 
|  | 108 | pkg_list['lgp'] = \ | 
|  | 109 | "locale-base-en-us locale-base-en-gb" | 
|  | 110 | elif image_rootfs.find("core-image-minimal") > 0: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 111 | pkg_list[self.PKG_TYPE_MUST_INSTALL] = "packagegroup-core-boot" | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 112 |  | 
|  | 113 | with open(self.initial_manifest, "w+") as manifest: | 
|  | 114 | manifest.write(self.initial_manifest_file_header) | 
|  | 115 |  | 
|  | 116 | for pkg_type in pkg_list: | 
|  | 117 | for pkg in pkg_list[pkg_type].split(): | 
|  | 118 | manifest.write("%s,%s\n" % (pkg_type, pkg)) | 
|  | 119 |  | 
|  | 120 | """ | 
|  | 121 | This will create the initial manifest which will be used by Rootfs class to | 
|  | 122 | generate the rootfs | 
|  | 123 | """ | 
|  | 124 | @abstractmethod | 
|  | 125 | def create_initial(self): | 
|  | 126 | pass | 
|  | 127 |  | 
|  | 128 | """ | 
|  | 129 | This creates the manifest after everything has been installed. | 
|  | 130 | """ | 
|  | 131 | @abstractmethod | 
|  | 132 | def create_final(self): | 
|  | 133 | pass | 
|  | 134 |  | 
|  | 135 | """ | 
|  | 136 | This creates the manifest after the package in initial manifest has been | 
|  | 137 | dummy installed. It lists all *to be installed* packages. There is no real | 
|  | 138 | installation, just a test. | 
|  | 139 | """ | 
|  | 140 | @abstractmethod | 
|  | 141 | def create_full(self, pm): | 
|  | 142 | pass | 
|  | 143 |  | 
|  | 144 | """ | 
|  | 145 | The following function parses an initial manifest and returns a dictionary | 
|  | 146 | object with the must install, attempt only, multilib and language packages. | 
|  | 147 | """ | 
|  | 148 | def parse_initial_manifest(self): | 
|  | 149 | pkgs = dict() | 
|  | 150 |  | 
|  | 151 | with open(self.initial_manifest) as manifest: | 
|  | 152 | for line in manifest.read().split('\n'): | 
|  | 153 | comment = re.match("^#.*", line) | 
|  | 154 | pattern = "^(%s|%s|%s|%s),(.*)$" % \ | 
|  | 155 | (self.PKG_TYPE_MUST_INSTALL, | 
|  | 156 | self.PKG_TYPE_ATTEMPT_ONLY, | 
|  | 157 | self.PKG_TYPE_MULTILIB, | 
|  | 158 | self.PKG_TYPE_LANGUAGE) | 
|  | 159 | pkg = re.match(pattern, line) | 
|  | 160 |  | 
|  | 161 | if comment is not None: | 
|  | 162 | continue | 
|  | 163 |  | 
|  | 164 | if pkg is not None: | 
|  | 165 | pkg_type = pkg.group(1) | 
|  | 166 | pkg_name = pkg.group(2) | 
|  | 167 |  | 
|  | 168 | if not pkg_type in pkgs: | 
|  | 169 | pkgs[pkg_type] = [pkg_name] | 
|  | 170 | else: | 
|  | 171 | pkgs[pkg_type].append(pkg_name) | 
|  | 172 |  | 
|  | 173 | return pkgs | 
|  | 174 |  | 
|  | 175 | ''' | 
|  | 176 | This following function parses a full manifest and return a list | 
|  | 177 | object with packages. | 
|  | 178 | ''' | 
|  | 179 | def parse_full_manifest(self): | 
|  | 180 | installed_pkgs = list() | 
|  | 181 | if not os.path.exists(self.full_manifest): | 
|  | 182 | bb.note('full manifest not exist') | 
|  | 183 | return installed_pkgs | 
|  | 184 |  | 
|  | 185 | with open(self.full_manifest, 'r') as manifest: | 
|  | 186 | for pkg in manifest.read().split('\n'): | 
|  | 187 | installed_pkgs.append(pkg.strip()) | 
|  | 188 |  | 
|  | 189 | return installed_pkgs | 
|  | 190 |  | 
|  | 191 |  | 
|  | 192 | class RpmManifest(Manifest): | 
|  | 193 | """ | 
|  | 194 | Returns a dictionary object with mip and mlp packages. | 
|  | 195 | """ | 
|  | 196 | def _split_multilib(self, pkg_list): | 
|  | 197 | pkgs = dict() | 
|  | 198 |  | 
|  | 199 | for pkg in pkg_list.split(): | 
|  | 200 | pkg_type = self.PKG_TYPE_MUST_INSTALL | 
|  | 201 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 202 | ml_variants = self.d.getVar('MULTILIB_VARIANTS').split() | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 203 |  | 
|  | 204 | for ml_variant in ml_variants: | 
|  | 205 | if pkg.startswith(ml_variant + '-'): | 
|  | 206 | pkg_type = self.PKG_TYPE_MULTILIB | 
|  | 207 |  | 
|  | 208 | if not pkg_type in pkgs: | 
|  | 209 | pkgs[pkg_type] = pkg | 
|  | 210 | else: | 
|  | 211 | pkgs[pkg_type] += " " + pkg | 
|  | 212 |  | 
|  | 213 | return pkgs | 
|  | 214 |  | 
|  | 215 | def create_initial(self): | 
|  | 216 | pkgs = dict() | 
|  | 217 |  | 
|  | 218 | with open(self.initial_manifest, "w+") as manifest: | 
|  | 219 | manifest.write(self.initial_manifest_file_header) | 
|  | 220 |  | 
|  | 221 | for var in self.var_maps[self.manifest_type]: | 
|  | 222 | if var in self.vars_to_split: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 223 | split_pkgs = self._split_multilib(self.d.getVar(var)) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 224 | if split_pkgs is not None: | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 225 | pkgs = dict(list(pkgs.items()) + list(split_pkgs.items())) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 226 | else: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 227 | pkg_list = self.d.getVar(var) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 228 | if pkg_list is not None: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 229 | pkgs[self.var_maps[self.manifest_type][var]] = self.d.getVar(var) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 230 |  | 
|  | 231 | for pkg_type in pkgs: | 
|  | 232 | for pkg in pkgs[pkg_type].split(): | 
|  | 233 | manifest.write("%s,%s\n" % (pkg_type, pkg)) | 
|  | 234 |  | 
|  | 235 | def create_final(self): | 
|  | 236 | pass | 
|  | 237 |  | 
|  | 238 | def create_full(self, pm): | 
|  | 239 | pass | 
|  | 240 |  | 
|  | 241 |  | 
|  | 242 | class OpkgManifest(Manifest): | 
|  | 243 | """ | 
|  | 244 | Returns a dictionary object with mip and mlp packages. | 
|  | 245 | """ | 
|  | 246 | def _split_multilib(self, pkg_list): | 
|  | 247 | pkgs = dict() | 
|  | 248 |  | 
|  | 249 | for pkg in pkg_list.split(): | 
|  | 250 | pkg_type = self.PKG_TYPE_MUST_INSTALL | 
|  | 251 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 252 | ml_variants = self.d.getVar('MULTILIB_VARIANTS').split() | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 253 |  | 
|  | 254 | for ml_variant in ml_variants: | 
|  | 255 | if pkg.startswith(ml_variant + '-'): | 
|  | 256 | pkg_type = self.PKG_TYPE_MULTILIB | 
|  | 257 |  | 
|  | 258 | if not pkg_type in pkgs: | 
|  | 259 | pkgs[pkg_type] = pkg | 
|  | 260 | else: | 
|  | 261 | pkgs[pkg_type] += " " + pkg | 
|  | 262 |  | 
|  | 263 | return pkgs | 
|  | 264 |  | 
|  | 265 | def create_initial(self): | 
|  | 266 | pkgs = dict() | 
|  | 267 |  | 
|  | 268 | with open(self.initial_manifest, "w+") as manifest: | 
|  | 269 | manifest.write(self.initial_manifest_file_header) | 
|  | 270 |  | 
|  | 271 | for var in self.var_maps[self.manifest_type]: | 
|  | 272 | if var in self.vars_to_split: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 273 | split_pkgs = self._split_multilib(self.d.getVar(var)) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 274 | if split_pkgs is not None: | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 275 | pkgs = dict(list(pkgs.items()) + list(split_pkgs.items())) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 276 | else: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 277 | pkg_list = self.d.getVar(var) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 278 | if pkg_list is not None: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 279 | pkgs[self.var_maps[self.manifest_type][var]] = self.d.getVar(var) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 280 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 281 | for pkg_type in sorted(pkgs): | 
|  | 282 | for pkg in sorted(pkgs[pkg_type].split()): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 283 | manifest.write("%s,%s\n" % (pkg_type, pkg)) | 
|  | 284 |  | 
|  | 285 | def create_final(self): | 
|  | 286 | pass | 
|  | 287 |  | 
|  | 288 | def create_full(self, pm): | 
|  | 289 | if not os.path.exists(self.initial_manifest): | 
|  | 290 | self.create_initial() | 
|  | 291 |  | 
|  | 292 | initial_manifest = self.parse_initial_manifest() | 
|  | 293 | pkgs_to_install = list() | 
|  | 294 | for pkg_type in initial_manifest: | 
|  | 295 | pkgs_to_install += initial_manifest[pkg_type] | 
|  | 296 | if len(pkgs_to_install) == 0: | 
|  | 297 | return | 
|  | 298 |  | 
|  | 299 | output = pm.dummy_install(pkgs_to_install) | 
|  | 300 |  | 
|  | 301 | with open(self.full_manifest, 'w+') as manifest: | 
|  | 302 | pkg_re = re.compile('^Installing ([^ ]+) [^ ].*') | 
|  | 303 | for line in set(output.split('\n')): | 
|  | 304 | m = pkg_re.match(line) | 
|  | 305 | if m: | 
|  | 306 | manifest.write(m.group(1) + '\n') | 
|  | 307 |  | 
|  | 308 | return | 
|  | 309 |  | 
|  | 310 |  | 
|  | 311 | class DpkgManifest(Manifest): | 
|  | 312 | def create_initial(self): | 
|  | 313 | with open(self.initial_manifest, "w+") as manifest: | 
|  | 314 | manifest.write(self.initial_manifest_file_header) | 
|  | 315 |  | 
|  | 316 | for var in self.var_maps[self.manifest_type]: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 317 | pkg_list = self.d.getVar(var) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 318 |  | 
|  | 319 | if pkg_list is None: | 
|  | 320 | continue | 
|  | 321 |  | 
|  | 322 | for pkg in pkg_list.split(): | 
|  | 323 | manifest.write("%s,%s\n" % | 
|  | 324 | (self.var_maps[self.manifest_type][var], pkg)) | 
|  | 325 |  | 
|  | 326 | def create_final(self): | 
|  | 327 | pass | 
|  | 328 |  | 
|  | 329 | def create_full(self, pm): | 
|  | 330 | pass | 
|  | 331 |  | 
|  | 332 |  | 
|  | 333 | def create_manifest(d, final_manifest=False, manifest_dir=None, | 
|  | 334 | manifest_type=Manifest.MANIFEST_TYPE_IMAGE): | 
|  | 335 | manifest_map = {'rpm': RpmManifest, | 
|  | 336 | 'ipk': OpkgManifest, | 
|  | 337 | 'deb': DpkgManifest} | 
|  | 338 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 339 | manifest = manifest_map[d.getVar('IMAGE_PKGTYPE')](d, manifest_dir, manifest_type) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 340 |  | 
|  | 341 | if final_manifest: | 
|  | 342 | manifest.create_final() | 
|  | 343 | else: | 
|  | 344 | manifest.create_initial() | 
|  | 345 |  | 
|  | 346 |  | 
|  | 347 | if __name__ == "__main__": | 
|  | 348 | pass |