1# 2# SPDX-License-Identifier: GPL-2.0-only 3# 4 5from abc import ABCMeta, abstractmethod 6import os 7import re 8import bb 9 10 11class Manifest(object, metaclass=ABCMeta): 12 """ 13 This is an abstract class. Do not instantiate this directly. 14 """ 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: 66 self.manifest_dir = self.d.getVar('SDK_DIR') 67 else: 68 self.manifest_dir = self.d.getVar('WORKDIR') 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): 89 image_rootfs = self.d.getVar('IMAGE_ROOTFS') 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: 111 pkg_list[self.PKG_TYPE_MUST_INSTALL] = "packagegroup-core-boot" 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 192class 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 202 ml_variants = self.d.getVar('MULTILIB_VARIANTS').split() 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: 223 split_pkgs = self._split_multilib(self.d.getVar(var)) 224 if split_pkgs is not None: 225 pkgs = dict(list(pkgs.items()) + list(split_pkgs.items())) 226 else: 227 pkg_list = self.d.getVar(var) 228 if pkg_list is not None: 229 pkgs[self.var_maps[self.manifest_type][var]] = self.d.getVar(var) 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 242class 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 252 ml_variants = self.d.getVar('MULTILIB_VARIANTS').split() 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: 273 split_pkgs = self._split_multilib(self.d.getVar(var)) 274 if split_pkgs is not None: 275 pkgs = dict(list(pkgs.items()) + list(split_pkgs.items())) 276 else: 277 pkg_list = self.d.getVar(var) 278 if pkg_list is not None: 279 pkgs[self.var_maps[self.manifest_type][var]] = self.d.getVar(var) 280 281 for pkg_type in sorted(pkgs): 282 for pkg in sorted(pkgs[pkg_type].split()): 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 311class 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]: 317 pkg_list = self.d.getVar(var) 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 333def 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 339 manifest = manifest_map[d.getVar('IMAGE_PKGTYPE')](d, manifest_dir, manifest_type) 340 341 if final_manifest: 342 manifest.create_final() 343 else: 344 manifest.create_initial() 345 346 347if __name__ == "__main__": 348 pass 349