1*4882a593Smuzhiyun# Copyright (C) 2016-2018 Wind River Systems, Inc. 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun 6*4882a593Smuzhiyunimport datetime 7*4882a593Smuzhiyun 8*4882a593Smuzhiyunimport logging 9*4882a593Smuzhiyunimport os 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunfrom collections import OrderedDict 12*4882a593Smuzhiyunfrom layerindexlib.plugin import LayerIndexPluginUrlError 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunlogger = logging.getLogger('BitBake.layerindexlib') 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun# Exceptions 17*4882a593Smuzhiyun 18*4882a593Smuzhiyunclass LayerIndexException(Exception): 19*4882a593Smuzhiyun '''LayerIndex Generic Exception''' 20*4882a593Smuzhiyun def __init__(self, message): 21*4882a593Smuzhiyun self.msg = message 22*4882a593Smuzhiyun Exception.__init__(self, message) 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun def __str__(self): 25*4882a593Smuzhiyun return self.msg 26*4882a593Smuzhiyun 27*4882a593Smuzhiyunclass LayerIndexUrlError(LayerIndexException): 28*4882a593Smuzhiyun '''Exception raised when unable to access a URL for some reason''' 29*4882a593Smuzhiyun def __init__(self, url, message=""): 30*4882a593Smuzhiyun if message: 31*4882a593Smuzhiyun msg = "Unable to access layerindex url %s: %s" % (url, message) 32*4882a593Smuzhiyun else: 33*4882a593Smuzhiyun msg = "Unable to access layerindex url %s" % url 34*4882a593Smuzhiyun self.url = url 35*4882a593Smuzhiyun LayerIndexException.__init__(self, msg) 36*4882a593Smuzhiyun 37*4882a593Smuzhiyunclass LayerIndexFetchError(LayerIndexException): 38*4882a593Smuzhiyun '''General layerindex fetcher exception when something fails''' 39*4882a593Smuzhiyun def __init__(self, url, message=""): 40*4882a593Smuzhiyun if message: 41*4882a593Smuzhiyun msg = "Unable to fetch layerindex url %s: %s" % (url, message) 42*4882a593Smuzhiyun else: 43*4882a593Smuzhiyun msg = "Unable to fetch layerindex url %s" % url 44*4882a593Smuzhiyun self.url = url 45*4882a593Smuzhiyun LayerIndexException.__init__(self, msg) 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun# Interface to the overall layerindex system 49*4882a593Smuzhiyun# the layer may contain one or more individual indexes 50*4882a593Smuzhiyunclass LayerIndex(): 51*4882a593Smuzhiyun def __init__(self, d): 52*4882a593Smuzhiyun if not d: 53*4882a593Smuzhiyun raise LayerIndexException("Must be initialized with bb.data.") 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun self.data = d 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun # List of LayerIndexObj 58*4882a593Smuzhiyun self.indexes = [] 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun self.plugins = [] 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun import bb.utils 63*4882a593Smuzhiyun bb.utils.load_plugins(logger, self.plugins, os.path.dirname(__file__)) 64*4882a593Smuzhiyun for plugin in self.plugins: 65*4882a593Smuzhiyun if hasattr(plugin, 'init'): 66*4882a593Smuzhiyun plugin.init(self) 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun def __add__(self, other): 69*4882a593Smuzhiyun newIndex = LayerIndex(self.data) 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun if self.__class__ != newIndex.__class__ or \ 72*4882a593Smuzhiyun other.__class__ != newIndex.__class__: 73*4882a593Smuzhiyun raise TypeError("Can not add different types.") 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun for indexEnt in self.indexes: 76*4882a593Smuzhiyun newIndex.indexes.append(indexEnt) 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun for indexEnt in other.indexes: 79*4882a593Smuzhiyun newIndex.indexes.append(indexEnt) 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun return newIndex 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun def _parse_params(self, params): 84*4882a593Smuzhiyun '''Take a parameter list, return a dictionary of parameters. 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun Expected to be called from the data of urllib.parse.urlparse(url).params 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun If there are two conflicting parameters, last in wins... 89*4882a593Smuzhiyun ''' 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun param_dict = {} 92*4882a593Smuzhiyun for param in params.split(';'): 93*4882a593Smuzhiyun if not param: 94*4882a593Smuzhiyun continue 95*4882a593Smuzhiyun item = param.split('=', 1) 96*4882a593Smuzhiyun logger.debug(item) 97*4882a593Smuzhiyun param_dict[item[0]] = item[1] 98*4882a593Smuzhiyun 99*4882a593Smuzhiyun return param_dict 100*4882a593Smuzhiyun 101*4882a593Smuzhiyun def _fetch_url(self, url, username=None, password=None, debuglevel=0): 102*4882a593Smuzhiyun '''Fetch data from a specific URL. 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun Fetch something from a specific URL. This is specifically designed to 105*4882a593Smuzhiyun fetch data from a layerindex-web instance, but may be useful for other 106*4882a593Smuzhiyun raw fetch actions. 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun It is not designed to be used to fetch recipe sources or similar. the 109*4882a593Smuzhiyun regular fetcher class should used for that. 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun It is the responsibility of the caller to check BB_NO_NETWORK and related 112*4882a593Smuzhiyun BB_ALLOWED_NETWORKS. 113*4882a593Smuzhiyun ''' 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun if not url: 116*4882a593Smuzhiyun raise LayerIndexUrlError(url, "empty url") 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun import urllib 119*4882a593Smuzhiyun from urllib.request import urlopen, Request 120*4882a593Smuzhiyun from urllib.parse import urlparse 121*4882a593Smuzhiyun 122*4882a593Smuzhiyun up = urlparse(url) 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun if username: 125*4882a593Smuzhiyun logger.debug("Configuring authentication for %s..." % url) 126*4882a593Smuzhiyun password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() 127*4882a593Smuzhiyun password_mgr.add_password(None, "%s://%s" % (up.scheme, up.netloc), username, password) 128*4882a593Smuzhiyun handler = urllib.request.HTTPBasicAuthHandler(password_mgr) 129*4882a593Smuzhiyun opener = urllib.request.build_opener(handler, urllib.request.HTTPSHandler(debuglevel=debuglevel)) 130*4882a593Smuzhiyun else: 131*4882a593Smuzhiyun opener = urllib.request.build_opener(urllib.request.HTTPSHandler(debuglevel=debuglevel)) 132*4882a593Smuzhiyun 133*4882a593Smuzhiyun urllib.request.install_opener(opener) 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun logger.debug("Fetching %s (%s)..." % (url, ["without authentication", "with authentication"][bool(username)])) 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun try: 138*4882a593Smuzhiyun res = urlopen(Request(url, headers={'User-Agent': 'Mozilla/5.0 (bitbake/lib/layerindex)'}, unverifiable=True)) 139*4882a593Smuzhiyun except urllib.error.HTTPError as e: 140*4882a593Smuzhiyun logger.debug("HTTP Error: %s: %s" % (e.code, e.reason)) 141*4882a593Smuzhiyun logger.debug(" Requested: %s" % (url)) 142*4882a593Smuzhiyun logger.debug(" Actual: %s" % (e.geturl())) 143*4882a593Smuzhiyun 144*4882a593Smuzhiyun if e.code == 404: 145*4882a593Smuzhiyun logger.debug("Request not found.") 146*4882a593Smuzhiyun raise LayerIndexFetchError(url, e) 147*4882a593Smuzhiyun else: 148*4882a593Smuzhiyun logger.debug("Headers:\n%s" % (e.headers)) 149*4882a593Smuzhiyun raise LayerIndexFetchError(url, e) 150*4882a593Smuzhiyun except OSError as e: 151*4882a593Smuzhiyun error = 0 152*4882a593Smuzhiyun reason = "" 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun # Process base OSError first... 155*4882a593Smuzhiyun if hasattr(e, 'errno'): 156*4882a593Smuzhiyun error = e.errno 157*4882a593Smuzhiyun reason = e.strerror 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun # Process gaierror (socket error) subclass if available. 160*4882a593Smuzhiyun if hasattr(e, 'reason') and hasattr(e.reason, 'errno') and hasattr(e.reason, 'strerror'): 161*4882a593Smuzhiyun error = e.reason.errno 162*4882a593Smuzhiyun reason = e.reason.strerror 163*4882a593Smuzhiyun if error == -2: 164*4882a593Smuzhiyun raise LayerIndexFetchError(url, "%s: %s" % (e, reason)) 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun if error and error != 0: 167*4882a593Smuzhiyun raise LayerIndexFetchError(url, "Unexpected exception: [Error %s] %s" % (error, reason)) 168*4882a593Smuzhiyun else: 169*4882a593Smuzhiyun raise LayerIndexFetchError(url, "Unable to fetch OSError exception: %s" % e) 170*4882a593Smuzhiyun 171*4882a593Smuzhiyun finally: 172*4882a593Smuzhiyun logger.debug("...fetching %s (%s), done." % (url, ["without authentication", "with authentication"][bool(username)])) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun return res 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun def load_layerindex(self, indexURI, load=['layerDependencies', 'recipes', 'machines', 'distros'], reload=False): 178*4882a593Smuzhiyun '''Load the layerindex. 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun indexURI - An index to load. (Use multiple calls to load multiple indexes) 181*4882a593Smuzhiyun 182*4882a593Smuzhiyun reload - If reload is True, then any previously loaded indexes will be forgotten. 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun load - List of elements to load. Default loads all items. 185*4882a593Smuzhiyun Note: plugs may ignore this. 186*4882a593Smuzhiyun 187*4882a593SmuzhiyunThe format of the indexURI: 188*4882a593Smuzhiyun 189*4882a593Smuzhiyun <url>;branch=<branch>;cache=<cache>;desc=<description> 190*4882a593Smuzhiyun 191*4882a593Smuzhiyun Note: the 'branch' parameter if set can select multiple branches by using 192*4882a593Smuzhiyun comma, such as 'branch=master,morty,pyro'. However, many operations only look 193*4882a593Smuzhiyun at the -first- branch specified! 194*4882a593Smuzhiyun 195*4882a593Smuzhiyun The cache value may be undefined, in this case a network failure will 196*4882a593Smuzhiyun result in an error, otherwise the system will look for a file of the cache 197*4882a593Smuzhiyun name and load that instead. 198*4882a593Smuzhiyun 199*4882a593Smuzhiyun For example: 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun https://layers.openembedded.org/layerindex/api/;branch=master;desc=OpenEmbedded%20Layer%20Index 202*4882a593Smuzhiyun cooker:// 203*4882a593Smuzhiyun''' 204*4882a593Smuzhiyun if reload: 205*4882a593Smuzhiyun self.indexes = [] 206*4882a593Smuzhiyun 207*4882a593Smuzhiyun logger.debug('Loading: %s' % indexURI) 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun if not self.plugins: 210*4882a593Smuzhiyun raise LayerIndexException("No LayerIndex Plugins available") 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun for plugin in self.plugins: 213*4882a593Smuzhiyun # Check if the plugin was initialized 214*4882a593Smuzhiyun logger.debug('Trying %s' % plugin.__class__) 215*4882a593Smuzhiyun if not hasattr(plugin, 'type') or not plugin.type: 216*4882a593Smuzhiyun continue 217*4882a593Smuzhiyun try: 218*4882a593Smuzhiyun # TODO: Implement 'cache', for when the network is not available 219*4882a593Smuzhiyun indexEnt = plugin.load_index(indexURI, load) 220*4882a593Smuzhiyun break 221*4882a593Smuzhiyun except LayerIndexPluginUrlError as e: 222*4882a593Smuzhiyun logger.debug("%s doesn't support %s" % (plugin.type, e.url)) 223*4882a593Smuzhiyun except NotImplementedError: 224*4882a593Smuzhiyun pass 225*4882a593Smuzhiyun else: 226*4882a593Smuzhiyun logger.debug("No plugins support %s" % indexURI) 227*4882a593Smuzhiyun raise LayerIndexException("No plugins support %s" % indexURI) 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun # Mark CONFIG data as something we've added... 230*4882a593Smuzhiyun indexEnt.config['local'] = [] 231*4882a593Smuzhiyun indexEnt.config['local'].append('config') 232*4882a593Smuzhiyun 233*4882a593Smuzhiyun # No longer permit changes.. 234*4882a593Smuzhiyun indexEnt.lockData() 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun self.indexes.append(indexEnt) 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun def store_layerindex(self, indexURI, index=None): 239*4882a593Smuzhiyun '''Store one layerindex 240*4882a593Smuzhiyun 241*4882a593SmuzhiyunTypically this will be used to create a local cache file of a remote index. 242*4882a593Smuzhiyun 243*4882a593Smuzhiyun file://<path>;branch=<branch> 244*4882a593Smuzhiyun 245*4882a593SmuzhiyunWe can write out in either the restapi or django formats. The split option 246*4882a593Smuzhiyunwill write out the individual elements split by layer and related components. 247*4882a593Smuzhiyun''' 248*4882a593Smuzhiyun if not index: 249*4882a593Smuzhiyun logger.warning('No index to write, nothing to do.') 250*4882a593Smuzhiyun return 251*4882a593Smuzhiyun 252*4882a593Smuzhiyun if not self.plugins: 253*4882a593Smuzhiyun raise LayerIndexException("No LayerIndex Plugins available") 254*4882a593Smuzhiyun 255*4882a593Smuzhiyun for plugin in self.plugins: 256*4882a593Smuzhiyun # Check if the plugin was initialized 257*4882a593Smuzhiyun logger.debug('Trying %s' % plugin.__class__) 258*4882a593Smuzhiyun if not hasattr(plugin, 'type') or not plugin.type: 259*4882a593Smuzhiyun continue 260*4882a593Smuzhiyun try: 261*4882a593Smuzhiyun plugin.store_index(indexURI, index) 262*4882a593Smuzhiyun break 263*4882a593Smuzhiyun except LayerIndexPluginUrlError as e: 264*4882a593Smuzhiyun logger.debug("%s doesn't support %s" % (plugin.type, e.url)) 265*4882a593Smuzhiyun except NotImplementedError: 266*4882a593Smuzhiyun logger.debug("Store not implemented in %s" % plugin.type) 267*4882a593Smuzhiyun pass 268*4882a593Smuzhiyun else: 269*4882a593Smuzhiyun logger.debug("No plugins support %s" % indexURI) 270*4882a593Smuzhiyun raise LayerIndexException("No plugins support %s" % indexURI) 271*4882a593Smuzhiyun 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun def is_empty(self): 274*4882a593Smuzhiyun '''Return True or False if the index has any usable data. 275*4882a593Smuzhiyun 276*4882a593SmuzhiyunWe check the indexes entries to see if they have a branch set, as well as 277*4882a593SmuzhiyunlayerBranches set. If not, they are effectively blank.''' 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun found = False 280*4882a593Smuzhiyun for index in self.indexes: 281*4882a593Smuzhiyun if index.__bool__(): 282*4882a593Smuzhiyun found = True 283*4882a593Smuzhiyun break 284*4882a593Smuzhiyun return not found 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun 287*4882a593Smuzhiyun def find_vcs_url(self, vcs_url, branch=None): 288*4882a593Smuzhiyun '''Return the first layerBranch with the given vcs_url 289*4882a593Smuzhiyun 290*4882a593Smuzhiyun If a branch has not been specified, we will iterate over the branches in 291*4882a593Smuzhiyun the default configuration until the first vcs_url/branch match.''' 292*4882a593Smuzhiyun 293*4882a593Smuzhiyun for index in self.indexes: 294*4882a593Smuzhiyun logger.debug(' searching %s' % index.config['DESCRIPTION']) 295*4882a593Smuzhiyun layerBranch = index.find_vcs_url(vcs_url, [branch]) 296*4882a593Smuzhiyun if layerBranch: 297*4882a593Smuzhiyun return layerBranch 298*4882a593Smuzhiyun return None 299*4882a593Smuzhiyun 300*4882a593Smuzhiyun def find_collection(self, collection, version=None, branch=None): 301*4882a593Smuzhiyun '''Return the first layerBranch with the given collection name 302*4882a593Smuzhiyun 303*4882a593Smuzhiyun If a branch has not been specified, we will iterate over the branches in 304*4882a593Smuzhiyun the default configuration until the first collection/branch match.''' 305*4882a593Smuzhiyun 306*4882a593Smuzhiyun logger.debug('find_collection: %s (%s) %s' % (collection, version, branch)) 307*4882a593Smuzhiyun 308*4882a593Smuzhiyun if branch: 309*4882a593Smuzhiyun branches = [branch] 310*4882a593Smuzhiyun else: 311*4882a593Smuzhiyun branches = None 312*4882a593Smuzhiyun 313*4882a593Smuzhiyun for index in self.indexes: 314*4882a593Smuzhiyun logger.debug(' searching %s' % index.config['DESCRIPTION']) 315*4882a593Smuzhiyun layerBranch = index.find_collection(collection, version, branches) 316*4882a593Smuzhiyun if layerBranch: 317*4882a593Smuzhiyun return layerBranch 318*4882a593Smuzhiyun else: 319*4882a593Smuzhiyun logger.debug('Collection %s (%s) not found for branch (%s)' % (collection, version, branch)) 320*4882a593Smuzhiyun return None 321*4882a593Smuzhiyun 322*4882a593Smuzhiyun def find_layerbranch(self, name, branch=None): 323*4882a593Smuzhiyun '''Return the layerBranch item for a given name and branch 324*4882a593Smuzhiyun 325*4882a593Smuzhiyun If a branch has not been specified, we will iterate over the branches in 326*4882a593Smuzhiyun the default configuration until the first name/branch match.''' 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun if branch: 329*4882a593Smuzhiyun branches = [branch] 330*4882a593Smuzhiyun else: 331*4882a593Smuzhiyun branches = None 332*4882a593Smuzhiyun 333*4882a593Smuzhiyun for index in self.indexes: 334*4882a593Smuzhiyun layerBranch = index.find_layerbranch(name, branches) 335*4882a593Smuzhiyun if layerBranch: 336*4882a593Smuzhiyun return layerBranch 337*4882a593Smuzhiyun return None 338*4882a593Smuzhiyun 339*4882a593Smuzhiyun def find_dependencies(self, names=None, layerbranches=None, ignores=None): 340*4882a593Smuzhiyun '''Return a tuple of all dependencies and valid items for the list of (layer) names 341*4882a593Smuzhiyun 342*4882a593Smuzhiyun The dependency scanning happens depth-first. The returned 343*4882a593Smuzhiyun dependencies should be in the best order to define bblayers. 344*4882a593Smuzhiyun 345*4882a593Smuzhiyun names - list of layer names (searching layerItems) 346*4882a593Smuzhiyun branches - when specified (with names) only this list of branches are evaluated 347*4882a593Smuzhiyun 348*4882a593Smuzhiyun layerbranches - list of layerbranches to resolve dependencies 349*4882a593Smuzhiyun 350*4882a593Smuzhiyun ignores - list of layer names to ignore 351*4882a593Smuzhiyun 352*4882a593Smuzhiyun return: (dependencies, invalid) 353*4882a593Smuzhiyun 354*4882a593Smuzhiyun dependencies[LayerItem.name] = [ LayerBranch, LayerDependency1, LayerDependency2, ... ] 355*4882a593Smuzhiyun invalid = [ LayerItem.name1, LayerItem.name2, ... ] 356*4882a593Smuzhiyun ''' 357*4882a593Smuzhiyun 358*4882a593Smuzhiyun invalid = [] 359*4882a593Smuzhiyun 360*4882a593Smuzhiyun # Convert name/branch to layerbranches 361*4882a593Smuzhiyun if layerbranches is None: 362*4882a593Smuzhiyun layerbranches = [] 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun for name in names: 365*4882a593Smuzhiyun if ignores and name in ignores: 366*4882a593Smuzhiyun continue 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun for index in self.indexes: 369*4882a593Smuzhiyun layerbranch = index.find_layerbranch(name) 370*4882a593Smuzhiyun if not layerbranch: 371*4882a593Smuzhiyun # Not in this index, hopefully it's in another... 372*4882a593Smuzhiyun continue 373*4882a593Smuzhiyun layerbranches.append(layerbranch) 374*4882a593Smuzhiyun break 375*4882a593Smuzhiyun else: 376*4882a593Smuzhiyun invalid.append(name) 377*4882a593Smuzhiyun 378*4882a593Smuzhiyun 379*4882a593Smuzhiyun def _resolve_dependencies(layerbranches, ignores, dependencies, invalid, processed=None): 380*4882a593Smuzhiyun for layerbranch in layerbranches: 381*4882a593Smuzhiyun if ignores and layerbranch.layer.name in ignores: 382*4882a593Smuzhiyun continue 383*4882a593Smuzhiyun 384*4882a593Smuzhiyun # Get a list of dependencies and then recursively process them 385*4882a593Smuzhiyun for layerdependency in layerbranch.index.layerDependencies_layerBranchId[layerbranch.id]: 386*4882a593Smuzhiyun deplayerbranch = layerdependency.dependency_layerBranch 387*4882a593Smuzhiyun 388*4882a593Smuzhiyun if ignores and deplayerbranch.layer.name in ignores: 389*4882a593Smuzhiyun continue 390*4882a593Smuzhiyun 391*4882a593Smuzhiyun # Since this is depth first, we need to know what we're currently processing 392*4882a593Smuzhiyun # in order to avoid infinite recursion on a loop. 393*4882a593Smuzhiyun if processed and deplayerbranch.layer.name in processed: 394*4882a593Smuzhiyun # We have found a recursion... 395*4882a593Smuzhiyun logger.warning('Circular layer dependency found: %s -> %s' % (processed, deplayerbranch.layer.name)) 396*4882a593Smuzhiyun continue 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun # This little block is why we can't re-use the LayerIndexObj version, 399*4882a593Smuzhiyun # we must be able to satisfy each dependencies across layer indexes and 400*4882a593Smuzhiyun # use the layer index order for priority. (r stands for replacement below) 401*4882a593Smuzhiyun 402*4882a593Smuzhiyun # If this is the primary index, we can fast path and skip this 403*4882a593Smuzhiyun if deplayerbranch.index != self.indexes[0]: 404*4882a593Smuzhiyun # Is there an entry in a prior index for this collection/version? 405*4882a593Smuzhiyun rdeplayerbranch = self.find_collection( 406*4882a593Smuzhiyun collection=deplayerbranch.collection, 407*4882a593Smuzhiyun version=deplayerbranch.version 408*4882a593Smuzhiyun ) 409*4882a593Smuzhiyun if rdeplayerbranch != deplayerbranch: 410*4882a593Smuzhiyun logger.debug('Replaced %s:%s:%s with %s:%s:%s' % \ 411*4882a593Smuzhiyun (deplayerbranch.index.config['DESCRIPTION'], 412*4882a593Smuzhiyun deplayerbranch.branch.name, 413*4882a593Smuzhiyun deplayerbranch.layer.name, 414*4882a593Smuzhiyun rdeplayerbranch.index.config['DESCRIPTION'], 415*4882a593Smuzhiyun rdeplayerbranch.branch.name, 416*4882a593Smuzhiyun rdeplayerbranch.layer.name)) 417*4882a593Smuzhiyun deplayerbranch = rdeplayerbranch 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun # New dependency, we need to resolve it now... depth-first 420*4882a593Smuzhiyun if deplayerbranch.layer.name not in dependencies: 421*4882a593Smuzhiyun # Avoid recursion on this branch. 422*4882a593Smuzhiyun # We copy so we don't end up polluting the depth-first branch with other 423*4882a593Smuzhiyun # branches. Duplication between individual branches IS expected and 424*4882a593Smuzhiyun # handled by 'dependencies' processing. 425*4882a593Smuzhiyun if not processed: 426*4882a593Smuzhiyun local_processed = [] 427*4882a593Smuzhiyun else: 428*4882a593Smuzhiyun local_processed = processed.copy() 429*4882a593Smuzhiyun local_processed.append(deplayerbranch.layer.name) 430*4882a593Smuzhiyun 431*4882a593Smuzhiyun (dependencies, invalid) = _resolve_dependencies([deplayerbranch], ignores, dependencies, invalid, local_processed) 432*4882a593Smuzhiyun 433*4882a593Smuzhiyun if deplayerbranch.layer.name not in dependencies: 434*4882a593Smuzhiyun dependencies[deplayerbranch.layer.name] = [deplayerbranch, layerdependency] 435*4882a593Smuzhiyun else: 436*4882a593Smuzhiyun if layerdependency not in dependencies[deplayerbranch.layer.name]: 437*4882a593Smuzhiyun dependencies[deplayerbranch.layer.name].append(layerdependency) 438*4882a593Smuzhiyun 439*4882a593Smuzhiyun return (dependencies, invalid) 440*4882a593Smuzhiyun 441*4882a593Smuzhiyun # OK, resolve this one... 442*4882a593Smuzhiyun dependencies = OrderedDict() 443*4882a593Smuzhiyun (dependencies, invalid) = _resolve_dependencies(layerbranches, ignores, dependencies, invalid) 444*4882a593Smuzhiyun 445*4882a593Smuzhiyun for layerbranch in layerbranches: 446*4882a593Smuzhiyun if layerbranch.layer.name not in dependencies: 447*4882a593Smuzhiyun dependencies[layerbranch.layer.name] = [layerbranch] 448*4882a593Smuzhiyun 449*4882a593Smuzhiyun return (dependencies, invalid) 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun 452*4882a593Smuzhiyun def list_obj(self, object): 453*4882a593Smuzhiyun '''Print via the plain logger object information 454*4882a593Smuzhiyun 455*4882a593SmuzhiyunThis function is used to implement debugging and provide the user info. 456*4882a593Smuzhiyun''' 457*4882a593Smuzhiyun for lix in self.indexes: 458*4882a593Smuzhiyun if not hasattr(lix, object): 459*4882a593Smuzhiyun continue 460*4882a593Smuzhiyun 461*4882a593Smuzhiyun logger.plain ('') 462*4882a593Smuzhiyun logger.plain ('Index: %s' % lix.config['DESCRIPTION']) 463*4882a593Smuzhiyun 464*4882a593Smuzhiyun output = [] 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun if object == 'branches': 467*4882a593Smuzhiyun logger.plain ('%s %s %s' % ('{:26}'.format('branch'), '{:34}'.format('description'), '{:22}'.format('bitbake branch'))) 468*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 469*4882a593Smuzhiyun for branchid in lix.branches: 470*4882a593Smuzhiyun output.append('%s %s %s' % ( 471*4882a593Smuzhiyun '{:26}'.format(lix.branches[branchid].name), 472*4882a593Smuzhiyun '{:34}'.format(lix.branches[branchid].short_description), 473*4882a593Smuzhiyun '{:22}'.format(lix.branches[branchid].bitbake_branch) 474*4882a593Smuzhiyun )) 475*4882a593Smuzhiyun for line in sorted(output): 476*4882a593Smuzhiyun logger.plain (line) 477*4882a593Smuzhiyun 478*4882a593Smuzhiyun continue 479*4882a593Smuzhiyun 480*4882a593Smuzhiyun if object == 'layerItems': 481*4882a593Smuzhiyun logger.plain ('%s %s' % ('{:26}'.format('layer'), '{:34}'.format('description'))) 482*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 483*4882a593Smuzhiyun for layerid in lix.layerItems: 484*4882a593Smuzhiyun output.append('%s %s' % ( 485*4882a593Smuzhiyun '{:26}'.format(lix.layerItems[layerid].name), 486*4882a593Smuzhiyun '{:34}'.format(lix.layerItems[layerid].summary) 487*4882a593Smuzhiyun )) 488*4882a593Smuzhiyun for line in sorted(output): 489*4882a593Smuzhiyun logger.plain (line) 490*4882a593Smuzhiyun 491*4882a593Smuzhiyun continue 492*4882a593Smuzhiyun 493*4882a593Smuzhiyun if object == 'layerBranches': 494*4882a593Smuzhiyun logger.plain ('%s %s %s' % ('{:26}'.format('layer'), '{:34}'.format('description'), '{:19}'.format('collection:version'))) 495*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 496*4882a593Smuzhiyun for layerbranchid in lix.layerBranches: 497*4882a593Smuzhiyun output.append('%s %s %s' % ( 498*4882a593Smuzhiyun '{:26}'.format(lix.layerBranches[layerbranchid].layer.name), 499*4882a593Smuzhiyun '{:34}'.format(lix.layerBranches[layerbranchid].layer.summary), 500*4882a593Smuzhiyun '{:19}'.format("%s:%s" % 501*4882a593Smuzhiyun (lix.layerBranches[layerbranchid].collection, 502*4882a593Smuzhiyun lix.layerBranches[layerbranchid].version) 503*4882a593Smuzhiyun ) 504*4882a593Smuzhiyun )) 505*4882a593Smuzhiyun for line in sorted(output): 506*4882a593Smuzhiyun logger.plain (line) 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun continue 509*4882a593Smuzhiyun 510*4882a593Smuzhiyun if object == 'layerDependencies': 511*4882a593Smuzhiyun logger.plain ('%s %s %s %s' % ('{:19}'.format('branch'), '{:26}'.format('layer'), '{:11}'.format('dependency'), '{:26}'.format('layer'))) 512*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 513*4882a593Smuzhiyun for layerDependency in lix.layerDependencies: 514*4882a593Smuzhiyun if not lix.layerDependencies[layerDependency].dependency_layerBranch: 515*4882a593Smuzhiyun continue 516*4882a593Smuzhiyun 517*4882a593Smuzhiyun output.append('%s %s %s %s' % ( 518*4882a593Smuzhiyun '{:19}'.format(lix.layerDependencies[layerDependency].layerbranch.branch.name), 519*4882a593Smuzhiyun '{:26}'.format(lix.layerDependencies[layerDependency].layerbranch.layer.name), 520*4882a593Smuzhiyun '{:11}'.format('requires' if lix.layerDependencies[layerDependency].required else 'recommends'), 521*4882a593Smuzhiyun '{:26}'.format(lix.layerDependencies[layerDependency].dependency_layerBranch.layer.name) 522*4882a593Smuzhiyun )) 523*4882a593Smuzhiyun for line in sorted(output): 524*4882a593Smuzhiyun logger.plain (line) 525*4882a593Smuzhiyun 526*4882a593Smuzhiyun continue 527*4882a593Smuzhiyun 528*4882a593Smuzhiyun if object == 'recipes': 529*4882a593Smuzhiyun logger.plain ('%s %s %s' % ('{:20}'.format('recipe'), '{:10}'.format('version'), 'layer')) 530*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 531*4882a593Smuzhiyun output = [] 532*4882a593Smuzhiyun for recipe in lix.recipes: 533*4882a593Smuzhiyun output.append('%s %s %s' % ( 534*4882a593Smuzhiyun '{:30}'.format(lix.recipes[recipe].pn), 535*4882a593Smuzhiyun '{:30}'.format(lix.recipes[recipe].pv), 536*4882a593Smuzhiyun lix.recipes[recipe].layer.name 537*4882a593Smuzhiyun )) 538*4882a593Smuzhiyun for line in sorted(output): 539*4882a593Smuzhiyun logger.plain (line) 540*4882a593Smuzhiyun 541*4882a593Smuzhiyun continue 542*4882a593Smuzhiyun 543*4882a593Smuzhiyun if object == 'machines': 544*4882a593Smuzhiyun logger.plain ('%s %s %s' % ('{:24}'.format('machine'), '{:34}'.format('description'), '{:19}'.format('layer'))) 545*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 546*4882a593Smuzhiyun for machine in lix.machines: 547*4882a593Smuzhiyun output.append('%s %s %s' % ( 548*4882a593Smuzhiyun '{:24}'.format(lix.machines[machine].name), 549*4882a593Smuzhiyun '{:34}'.format(lix.machines[machine].description)[:34], 550*4882a593Smuzhiyun '{:19}'.format(lix.machines[machine].layerbranch.layer.name) 551*4882a593Smuzhiyun )) 552*4882a593Smuzhiyun for line in sorted(output): 553*4882a593Smuzhiyun logger.plain (line) 554*4882a593Smuzhiyun 555*4882a593Smuzhiyun continue 556*4882a593Smuzhiyun 557*4882a593Smuzhiyun if object == 'distros': 558*4882a593Smuzhiyun logger.plain ('%s %s %s' % ('{:24}'.format('distro'), '{:34}'.format('description'), '{:19}'.format('layer'))) 559*4882a593Smuzhiyun logger.plain ('{:-^80}'.format("")) 560*4882a593Smuzhiyun for distro in lix.distros: 561*4882a593Smuzhiyun output.append('%s %s %s' % ( 562*4882a593Smuzhiyun '{:24}'.format(lix.distros[distro].name), 563*4882a593Smuzhiyun '{:34}'.format(lix.distros[distro].description)[:34], 564*4882a593Smuzhiyun '{:19}'.format(lix.distros[distro].layerbranch.layer.name) 565*4882a593Smuzhiyun )) 566*4882a593Smuzhiyun for line in sorted(output): 567*4882a593Smuzhiyun logger.plain (line) 568*4882a593Smuzhiyun 569*4882a593Smuzhiyun continue 570*4882a593Smuzhiyun 571*4882a593Smuzhiyun logger.plain ('') 572*4882a593Smuzhiyun 573*4882a593Smuzhiyun 574*4882a593Smuzhiyun# This class holds a single layer index instance 575*4882a593Smuzhiyun# The LayerIndexObj is made up of dictionary of elements, such as: 576*4882a593Smuzhiyun# index['config'] - configuration data for this index 577*4882a593Smuzhiyun# index['branches'] - dictionary of Branch objects, by id number 578*4882a593Smuzhiyun# index['layerItems'] - dictionary of layerItem objects, by id number 579*4882a593Smuzhiyun# ...etc... (See: https://layers.openembedded.org/layerindex/api/) 580*4882a593Smuzhiyun# 581*4882a593Smuzhiyun# The class needs to manage the 'index' entries and allow easily adding 582*4882a593Smuzhiyun# of new items, as well as simply loading of the items. 583*4882a593Smuzhiyunclass LayerIndexObj(): 584*4882a593Smuzhiyun def __init__(self): 585*4882a593Smuzhiyun super().__setattr__('_index', {}) 586*4882a593Smuzhiyun super().__setattr__('_lock', False) 587*4882a593Smuzhiyun 588*4882a593Smuzhiyun def __bool__(self): 589*4882a593Smuzhiyun '''False if the index is effectively empty 590*4882a593Smuzhiyun 591*4882a593Smuzhiyun We check the index to see if it has a branch set, as well as 592*4882a593Smuzhiyun layerbranches set. If not, it is effectively blank.''' 593*4882a593Smuzhiyun 594*4882a593Smuzhiyun if not bool(self._index): 595*4882a593Smuzhiyun return False 596*4882a593Smuzhiyun 597*4882a593Smuzhiyun try: 598*4882a593Smuzhiyun if self.branches and self.layerBranches: 599*4882a593Smuzhiyun return True 600*4882a593Smuzhiyun except AttributeError: 601*4882a593Smuzhiyun pass 602*4882a593Smuzhiyun 603*4882a593Smuzhiyun return False 604*4882a593Smuzhiyun 605*4882a593Smuzhiyun def __getattr__(self, name): 606*4882a593Smuzhiyun if name.startswith('_'): 607*4882a593Smuzhiyun return super().__getattribute__(name) 608*4882a593Smuzhiyun 609*4882a593Smuzhiyun if name not in self._index: 610*4882a593Smuzhiyun raise AttributeError('%s not in index datastore' % name) 611*4882a593Smuzhiyun 612*4882a593Smuzhiyun return self._index[name] 613*4882a593Smuzhiyun 614*4882a593Smuzhiyun def __setattr__(self, name, value): 615*4882a593Smuzhiyun if self.isLocked(): 616*4882a593Smuzhiyun raise TypeError("Can not set attribute '%s': index is locked" % name) 617*4882a593Smuzhiyun 618*4882a593Smuzhiyun if name.startswith('_'): 619*4882a593Smuzhiyun super().__setattr__(name, value) 620*4882a593Smuzhiyun return 621*4882a593Smuzhiyun 622*4882a593Smuzhiyun self._index[name] = value 623*4882a593Smuzhiyun 624*4882a593Smuzhiyun def __delattr__(self, name): 625*4882a593Smuzhiyun if self.isLocked(): 626*4882a593Smuzhiyun raise TypeError("Can not delete attribute '%s': index is locked" % name) 627*4882a593Smuzhiyun 628*4882a593Smuzhiyun if name.startswith('_'): 629*4882a593Smuzhiyun super().__delattr__(name) 630*4882a593Smuzhiyun 631*4882a593Smuzhiyun self._index.pop(name) 632*4882a593Smuzhiyun 633*4882a593Smuzhiyun def lockData(self): 634*4882a593Smuzhiyun '''Lock data object (make it readonly)''' 635*4882a593Smuzhiyun super().__setattr__("_lock", True) 636*4882a593Smuzhiyun 637*4882a593Smuzhiyun def unlockData(self): 638*4882a593Smuzhiyun '''unlock data object (make it readonly)''' 639*4882a593Smuzhiyun super().__setattr__("_lock", False) 640*4882a593Smuzhiyun 641*4882a593Smuzhiyun # When the data is unlocked, we have to clear the caches, as 642*4882a593Smuzhiyun # modification is allowed! 643*4882a593Smuzhiyun del(self._layerBranches_layerId_branchId) 644*4882a593Smuzhiyun del(self._layerDependencies_layerBranchId) 645*4882a593Smuzhiyun del(self._layerBranches_vcsUrl) 646*4882a593Smuzhiyun 647*4882a593Smuzhiyun def isLocked(self): 648*4882a593Smuzhiyun '''Is this object locked (readonly)?''' 649*4882a593Smuzhiyun return self._lock 650*4882a593Smuzhiyun 651*4882a593Smuzhiyun def add_element(self, indexname, objs): 652*4882a593Smuzhiyun '''Add a layer index object to index.<indexname>''' 653*4882a593Smuzhiyun if indexname not in self._index: 654*4882a593Smuzhiyun self._index[indexname] = {} 655*4882a593Smuzhiyun 656*4882a593Smuzhiyun for obj in objs: 657*4882a593Smuzhiyun if obj.id in self._index[indexname]: 658*4882a593Smuzhiyun if self._index[indexname][obj.id] == obj: 659*4882a593Smuzhiyun continue 660*4882a593Smuzhiyun raise LayerIndexException('Conflict adding object %s(%s) to index' % (indexname, obj.id)) 661*4882a593Smuzhiyun self._index[indexname][obj.id] = obj 662*4882a593Smuzhiyun 663*4882a593Smuzhiyun def add_raw_element(self, indexname, objtype, rawobjs): 664*4882a593Smuzhiyun '''Convert a raw layer index data item to a layer index item object and add to the index''' 665*4882a593Smuzhiyun objs = [] 666*4882a593Smuzhiyun for entry in rawobjs: 667*4882a593Smuzhiyun objs.append(objtype(self, entry)) 668*4882a593Smuzhiyun self.add_element(indexname, objs) 669*4882a593Smuzhiyun 670*4882a593Smuzhiyun # Quick lookup table for searching layerId and branchID combos 671*4882a593Smuzhiyun @property 672*4882a593Smuzhiyun def layerBranches_layerId_branchId(self): 673*4882a593Smuzhiyun def createCache(self): 674*4882a593Smuzhiyun cache = {} 675*4882a593Smuzhiyun for layerbranchid in self.layerBranches: 676*4882a593Smuzhiyun layerbranch = self.layerBranches[layerbranchid] 677*4882a593Smuzhiyun cache["%s:%s" % (layerbranch.layer_id, layerbranch.branch_id)] = layerbranch 678*4882a593Smuzhiyun return cache 679*4882a593Smuzhiyun 680*4882a593Smuzhiyun if self.isLocked(): 681*4882a593Smuzhiyun cache = getattr(self, '_layerBranches_layerId_branchId', None) 682*4882a593Smuzhiyun else: 683*4882a593Smuzhiyun cache = None 684*4882a593Smuzhiyun 685*4882a593Smuzhiyun if not cache: 686*4882a593Smuzhiyun cache = createCache(self) 687*4882a593Smuzhiyun 688*4882a593Smuzhiyun if self.isLocked(): 689*4882a593Smuzhiyun super().__setattr__('_layerBranches_layerId_branchId', cache) 690*4882a593Smuzhiyun 691*4882a593Smuzhiyun return cache 692*4882a593Smuzhiyun 693*4882a593Smuzhiyun # Quick lookup table for finding all dependencies of a layerBranch 694*4882a593Smuzhiyun @property 695*4882a593Smuzhiyun def layerDependencies_layerBranchId(self): 696*4882a593Smuzhiyun def createCache(self): 697*4882a593Smuzhiyun cache = {} 698*4882a593Smuzhiyun # This ensures empty lists for all branchids 699*4882a593Smuzhiyun for layerbranchid in self.layerBranches: 700*4882a593Smuzhiyun cache[layerbranchid] = [] 701*4882a593Smuzhiyun 702*4882a593Smuzhiyun for layerdependencyid in self.layerDependencies: 703*4882a593Smuzhiyun layerdependency = self.layerDependencies[layerdependencyid] 704*4882a593Smuzhiyun cache[layerdependency.layerbranch_id].append(layerdependency) 705*4882a593Smuzhiyun return cache 706*4882a593Smuzhiyun 707*4882a593Smuzhiyun if self.isLocked(): 708*4882a593Smuzhiyun cache = getattr(self, '_layerDependencies_layerBranchId', None) 709*4882a593Smuzhiyun else: 710*4882a593Smuzhiyun cache = None 711*4882a593Smuzhiyun 712*4882a593Smuzhiyun if not cache: 713*4882a593Smuzhiyun cache = createCache(self) 714*4882a593Smuzhiyun 715*4882a593Smuzhiyun if self.isLocked(): 716*4882a593Smuzhiyun super().__setattr__('_layerDependencies_layerBranchId', cache) 717*4882a593Smuzhiyun 718*4882a593Smuzhiyun return cache 719*4882a593Smuzhiyun 720*4882a593Smuzhiyun # Quick lookup table for finding all instances of a vcs_url 721*4882a593Smuzhiyun @property 722*4882a593Smuzhiyun def layerBranches_vcsUrl(self): 723*4882a593Smuzhiyun def createCache(self): 724*4882a593Smuzhiyun cache = {} 725*4882a593Smuzhiyun for layerbranchid in self.layerBranches: 726*4882a593Smuzhiyun layerbranch = self.layerBranches[layerbranchid] 727*4882a593Smuzhiyun if layerbranch.layer.vcs_url not in cache: 728*4882a593Smuzhiyun cache[layerbranch.layer.vcs_url] = [layerbranch] 729*4882a593Smuzhiyun else: 730*4882a593Smuzhiyun cache[layerbranch.layer.vcs_url].append(layerbranch) 731*4882a593Smuzhiyun return cache 732*4882a593Smuzhiyun 733*4882a593Smuzhiyun if self.isLocked(): 734*4882a593Smuzhiyun cache = getattr(self, '_layerBranches_vcsUrl', None) 735*4882a593Smuzhiyun else: 736*4882a593Smuzhiyun cache = None 737*4882a593Smuzhiyun 738*4882a593Smuzhiyun if not cache: 739*4882a593Smuzhiyun cache = createCache(self) 740*4882a593Smuzhiyun 741*4882a593Smuzhiyun if self.isLocked(): 742*4882a593Smuzhiyun super().__setattr__('_layerBranches_vcsUrl', cache) 743*4882a593Smuzhiyun 744*4882a593Smuzhiyun return cache 745*4882a593Smuzhiyun 746*4882a593Smuzhiyun 747*4882a593Smuzhiyun def find_vcs_url(self, vcs_url, branches=None): 748*4882a593Smuzhiyun ''''Return the first layerBranch with the given vcs_url 749*4882a593Smuzhiyun 750*4882a593Smuzhiyun If a list of branches has not been specified, we will iterate on 751*4882a593Smuzhiyun all branches until the first vcs_url is found.''' 752*4882a593Smuzhiyun 753*4882a593Smuzhiyun if not self.__bool__(): 754*4882a593Smuzhiyun return None 755*4882a593Smuzhiyun 756*4882a593Smuzhiyun for layerbranch in self.layerBranches_vcsUrl: 757*4882a593Smuzhiyun if branches and layerbranch.branch.name not in branches: 758*4882a593Smuzhiyun continue 759*4882a593Smuzhiyun 760*4882a593Smuzhiyun return layerbranch 761*4882a593Smuzhiyun 762*4882a593Smuzhiyun return None 763*4882a593Smuzhiyun 764*4882a593Smuzhiyun 765*4882a593Smuzhiyun def find_collection(self, collection, version=None, branches=None): 766*4882a593Smuzhiyun '''Return the first layerBranch with the given collection name 767*4882a593Smuzhiyun 768*4882a593Smuzhiyun If a list of branches has not been specified, we will iterate on 769*4882a593Smuzhiyun all branches until the first collection is found.''' 770*4882a593Smuzhiyun 771*4882a593Smuzhiyun if not self.__bool__(): 772*4882a593Smuzhiyun return None 773*4882a593Smuzhiyun 774*4882a593Smuzhiyun for layerbranchid in self.layerBranches: 775*4882a593Smuzhiyun layerbranch = self.layerBranches[layerbranchid] 776*4882a593Smuzhiyun if branches and layerbranch.branch.name not in branches: 777*4882a593Smuzhiyun continue 778*4882a593Smuzhiyun 779*4882a593Smuzhiyun if layerbranch.collection == collection and \ 780*4882a593Smuzhiyun (version is None or version == layerbranch.version): 781*4882a593Smuzhiyun return layerbranch 782*4882a593Smuzhiyun 783*4882a593Smuzhiyun return None 784*4882a593Smuzhiyun 785*4882a593Smuzhiyun 786*4882a593Smuzhiyun def find_layerbranch(self, name, branches=None): 787*4882a593Smuzhiyun '''Return the first layerbranch whose layer name matches 788*4882a593Smuzhiyun 789*4882a593Smuzhiyun If a list of branches has not been specified, we will iterate on 790*4882a593Smuzhiyun all branches until the first layer with that name is found.''' 791*4882a593Smuzhiyun 792*4882a593Smuzhiyun if not self.__bool__(): 793*4882a593Smuzhiyun return None 794*4882a593Smuzhiyun 795*4882a593Smuzhiyun for layerbranchid in self.layerBranches: 796*4882a593Smuzhiyun layerbranch = self.layerBranches[layerbranchid] 797*4882a593Smuzhiyun if branches and layerbranch.branch.name not in branches: 798*4882a593Smuzhiyun continue 799*4882a593Smuzhiyun 800*4882a593Smuzhiyun if layerbranch.layer.name == name: 801*4882a593Smuzhiyun return layerbranch 802*4882a593Smuzhiyun 803*4882a593Smuzhiyun return None 804*4882a593Smuzhiyun 805*4882a593Smuzhiyun def find_dependencies(self, names=None, branches=None, layerBranches=None, ignores=None): 806*4882a593Smuzhiyun '''Return a tuple of all dependencies and valid items for the list of (layer) names 807*4882a593Smuzhiyun 808*4882a593Smuzhiyun The dependency scanning happens depth-first. The returned 809*4882a593Smuzhiyun dependencies should be in the best order to define bblayers. 810*4882a593Smuzhiyun 811*4882a593Smuzhiyun names - list of layer names (searching layerItems) 812*4882a593Smuzhiyun branches - when specified (with names) only this list of branches are evaluated 813*4882a593Smuzhiyun 814*4882a593Smuzhiyun layerBranches - list of layerBranches to resolve dependencies 815*4882a593Smuzhiyun 816*4882a593Smuzhiyun ignores - list of layer names to ignore 817*4882a593Smuzhiyun 818*4882a593Smuzhiyun return: (dependencies, invalid) 819*4882a593Smuzhiyun 820*4882a593Smuzhiyun dependencies[LayerItem.name] = [ LayerBranch, LayerDependency1, LayerDependency2, ... ] 821*4882a593Smuzhiyun invalid = [ LayerItem.name1, LayerItem.name2, ... ]''' 822*4882a593Smuzhiyun 823*4882a593Smuzhiyun invalid = [] 824*4882a593Smuzhiyun 825*4882a593Smuzhiyun # Convert name/branch to layerBranches 826*4882a593Smuzhiyun if layerbranches is None: 827*4882a593Smuzhiyun layerbranches = [] 828*4882a593Smuzhiyun 829*4882a593Smuzhiyun for name in names: 830*4882a593Smuzhiyun if ignores and name in ignores: 831*4882a593Smuzhiyun continue 832*4882a593Smuzhiyun 833*4882a593Smuzhiyun layerbranch = self.find_layerbranch(name, branches) 834*4882a593Smuzhiyun if not layerbranch: 835*4882a593Smuzhiyun invalid.append(name) 836*4882a593Smuzhiyun else: 837*4882a593Smuzhiyun layerbranches.append(layerbranch) 838*4882a593Smuzhiyun 839*4882a593Smuzhiyun for layerbranch in layerbranches: 840*4882a593Smuzhiyun if layerbranch.index != self: 841*4882a593Smuzhiyun raise LayerIndexException("Can not resolve dependencies across indexes with this class function!") 842*4882a593Smuzhiyun 843*4882a593Smuzhiyun def _resolve_dependencies(layerbranches, ignores, dependencies, invalid): 844*4882a593Smuzhiyun for layerbranch in layerbranches: 845*4882a593Smuzhiyun if ignores and layerbranch.layer.name in ignores: 846*4882a593Smuzhiyun continue 847*4882a593Smuzhiyun 848*4882a593Smuzhiyun for layerdependency in layerbranch.index.layerDependencies_layerBranchId[layerbranch.id]: 849*4882a593Smuzhiyun deplayerbranch = layerdependency.dependency_layerBranch 850*4882a593Smuzhiyun 851*4882a593Smuzhiyun if ignores and deplayerbranch.layer.name in ignores: 852*4882a593Smuzhiyun continue 853*4882a593Smuzhiyun 854*4882a593Smuzhiyun # New dependency, we need to resolve it now... depth-first 855*4882a593Smuzhiyun if deplayerbranch.layer.name not in dependencies: 856*4882a593Smuzhiyun (dependencies, invalid) = _resolve_dependencies([deplayerbranch], ignores, dependencies, invalid) 857*4882a593Smuzhiyun 858*4882a593Smuzhiyun if deplayerbranch.layer.name not in dependencies: 859*4882a593Smuzhiyun dependencies[deplayerbranch.layer.name] = [deplayerbranch, layerdependency] 860*4882a593Smuzhiyun else: 861*4882a593Smuzhiyun if layerdependency not in dependencies[deplayerbranch.layer.name]: 862*4882a593Smuzhiyun dependencies[deplayerbranch.layer.name].append(layerdependency) 863*4882a593Smuzhiyun 864*4882a593Smuzhiyun return (dependencies, invalid) 865*4882a593Smuzhiyun 866*4882a593Smuzhiyun # OK, resolve this one... 867*4882a593Smuzhiyun dependencies = OrderedDict() 868*4882a593Smuzhiyun (dependencies, invalid) = _resolve_dependencies(layerbranches, ignores, dependencies, invalid) 869*4882a593Smuzhiyun 870*4882a593Smuzhiyun # Is this item already in the list, if not add it 871*4882a593Smuzhiyun for layerbranch in layerbranches: 872*4882a593Smuzhiyun if layerbranch.layer.name not in dependencies: 873*4882a593Smuzhiyun dependencies[layerbranch.layer.name] = [layerbranch] 874*4882a593Smuzhiyun 875*4882a593Smuzhiyun return (dependencies, invalid) 876*4882a593Smuzhiyun 877*4882a593Smuzhiyun 878*4882a593Smuzhiyun# Define a basic LayerIndexItemObj. This object forms the basis for all other 879*4882a593Smuzhiyun# objects. The raw Layer Index data is stored in the _data element, but we 880*4882a593Smuzhiyun# do not want users to access data directly. So wrap this and protect it 881*4882a593Smuzhiyun# from direct manipulation. 882*4882a593Smuzhiyun# 883*4882a593Smuzhiyun# It is up to the insantiators of the objects to fill them out, and once done 884*4882a593Smuzhiyun# lock the objects to prevent further accidently manipulation. 885*4882a593Smuzhiyun# 886*4882a593Smuzhiyun# Using the getattr, setattr and properties we can access and manipulate 887*4882a593Smuzhiyun# the data within the data element. 888*4882a593Smuzhiyunclass LayerIndexItemObj(): 889*4882a593Smuzhiyun def __init__(self, index, data=None, lock=False): 890*4882a593Smuzhiyun if data is None: 891*4882a593Smuzhiyun data = {} 892*4882a593Smuzhiyun 893*4882a593Smuzhiyun if type(data) != type(dict()): 894*4882a593Smuzhiyun raise TypeError('data (%s) is not a dict' % type(data)) 895*4882a593Smuzhiyun 896*4882a593Smuzhiyun super().__setattr__('_lock', lock) 897*4882a593Smuzhiyun super().__setattr__('index', index) 898*4882a593Smuzhiyun super().__setattr__('_data', data) 899*4882a593Smuzhiyun 900*4882a593Smuzhiyun def __eq__(self, other): 901*4882a593Smuzhiyun if self.__class__ != other.__class__: 902*4882a593Smuzhiyun return False 903*4882a593Smuzhiyun res=(self._data == other._data) 904*4882a593Smuzhiyun return res 905*4882a593Smuzhiyun 906*4882a593Smuzhiyun def __bool__(self): 907*4882a593Smuzhiyun return bool(self._data) 908*4882a593Smuzhiyun 909*4882a593Smuzhiyun def __getattr__(self, name): 910*4882a593Smuzhiyun # These are internal to THIS class, and not part of data 911*4882a593Smuzhiyun if name == "index" or name.startswith('_'): 912*4882a593Smuzhiyun return super().__getattribute__(name) 913*4882a593Smuzhiyun 914*4882a593Smuzhiyun if name not in self._data: 915*4882a593Smuzhiyun raise AttributeError('%s not in datastore' % name) 916*4882a593Smuzhiyun 917*4882a593Smuzhiyun return self._data[name] 918*4882a593Smuzhiyun 919*4882a593Smuzhiyun def _setattr(self, name, value, prop=True): 920*4882a593Smuzhiyun '''__setattr__ like function, but with control over property object behavior''' 921*4882a593Smuzhiyun if self.isLocked(): 922*4882a593Smuzhiyun raise TypeError("Can not set attribute '%s': Object data is locked" % name) 923*4882a593Smuzhiyun 924*4882a593Smuzhiyun if name.startswith('_'): 925*4882a593Smuzhiyun super().__setattr__(name, value) 926*4882a593Smuzhiyun return 927*4882a593Smuzhiyun 928*4882a593Smuzhiyun # Since __setattr__ runs before properties, we need to check if 929*4882a593Smuzhiyun # there is a setter property and then execute it 930*4882a593Smuzhiyun # ... or return self._data[name] 931*4882a593Smuzhiyun propertyobj = getattr(self.__class__, name, None) 932*4882a593Smuzhiyun if prop and isinstance(propertyobj, property): 933*4882a593Smuzhiyun if propertyobj.fset: 934*4882a593Smuzhiyun propertyobj.fset(self, value) 935*4882a593Smuzhiyun else: 936*4882a593Smuzhiyun raise AttributeError('Attribute %s is readonly, and may not be set' % name) 937*4882a593Smuzhiyun else: 938*4882a593Smuzhiyun self._data[name] = value 939*4882a593Smuzhiyun 940*4882a593Smuzhiyun def __setattr__(self, name, value): 941*4882a593Smuzhiyun self._setattr(name, value, prop=True) 942*4882a593Smuzhiyun 943*4882a593Smuzhiyun def _delattr(self, name, prop=True): 944*4882a593Smuzhiyun # Since __delattr__ runs before properties, we need to check if 945*4882a593Smuzhiyun # there is a deleter property and then execute it 946*4882a593Smuzhiyun # ... or we pop it ourselves.. 947*4882a593Smuzhiyun propertyobj = getattr(self.__class__, name, None) 948*4882a593Smuzhiyun if prop and isinstance(propertyobj, property): 949*4882a593Smuzhiyun if propertyobj.fdel: 950*4882a593Smuzhiyun propertyobj.fdel(self) 951*4882a593Smuzhiyun else: 952*4882a593Smuzhiyun raise AttributeError('Attribute %s is readonly, and may not be deleted' % name) 953*4882a593Smuzhiyun else: 954*4882a593Smuzhiyun self._data.pop(name) 955*4882a593Smuzhiyun 956*4882a593Smuzhiyun def __delattr__(self, name): 957*4882a593Smuzhiyun self._delattr(name, prop=True) 958*4882a593Smuzhiyun 959*4882a593Smuzhiyun def lockData(self): 960*4882a593Smuzhiyun '''Lock data object (make it readonly)''' 961*4882a593Smuzhiyun super().__setattr__("_lock", True) 962*4882a593Smuzhiyun 963*4882a593Smuzhiyun def unlockData(self): 964*4882a593Smuzhiyun '''unlock data object (make it readonly)''' 965*4882a593Smuzhiyun super().__setattr__("_lock", False) 966*4882a593Smuzhiyun 967*4882a593Smuzhiyun def isLocked(self): 968*4882a593Smuzhiyun '''Is this object locked (readonly)?''' 969*4882a593Smuzhiyun return self._lock 970*4882a593Smuzhiyun 971*4882a593Smuzhiyun# Branch object 972*4882a593Smuzhiyunclass Branch(LayerIndexItemObj): 973*4882a593Smuzhiyun def define_data(self, id, name, bitbake_branch, 974*4882a593Smuzhiyun short_description=None, sort_priority=1, 975*4882a593Smuzhiyun updates_enabled=True, updated=None, 976*4882a593Smuzhiyun update_environment=None): 977*4882a593Smuzhiyun self.id = id 978*4882a593Smuzhiyun self.name = name 979*4882a593Smuzhiyun self.bitbake_branch = bitbake_branch 980*4882a593Smuzhiyun self.short_description = short_description or name 981*4882a593Smuzhiyun self.sort_priority = sort_priority 982*4882a593Smuzhiyun self.updates_enabled = updates_enabled 983*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 984*4882a593Smuzhiyun self.update_environment = update_environment 985*4882a593Smuzhiyun 986*4882a593Smuzhiyun @property 987*4882a593Smuzhiyun def name(self): 988*4882a593Smuzhiyun return self.__getattr__('name') 989*4882a593Smuzhiyun 990*4882a593Smuzhiyun @name.setter 991*4882a593Smuzhiyun def name(self, value): 992*4882a593Smuzhiyun self._data['name'] = value 993*4882a593Smuzhiyun 994*4882a593Smuzhiyun if self.bitbake_branch == value: 995*4882a593Smuzhiyun self.bitbake_branch = "" 996*4882a593Smuzhiyun 997*4882a593Smuzhiyun @name.deleter 998*4882a593Smuzhiyun def name(self): 999*4882a593Smuzhiyun self._delattr('name', prop=False) 1000*4882a593Smuzhiyun 1001*4882a593Smuzhiyun @property 1002*4882a593Smuzhiyun def bitbake_branch(self): 1003*4882a593Smuzhiyun try: 1004*4882a593Smuzhiyun return self.__getattr__('bitbake_branch') 1005*4882a593Smuzhiyun except AttributeError: 1006*4882a593Smuzhiyun return self.name 1007*4882a593Smuzhiyun 1008*4882a593Smuzhiyun @bitbake_branch.setter 1009*4882a593Smuzhiyun def bitbake_branch(self, value): 1010*4882a593Smuzhiyun if self.name == value: 1011*4882a593Smuzhiyun self._data['bitbake_branch'] = "" 1012*4882a593Smuzhiyun else: 1013*4882a593Smuzhiyun self._data['bitbake_branch'] = value 1014*4882a593Smuzhiyun 1015*4882a593Smuzhiyun @bitbake_branch.deleter 1016*4882a593Smuzhiyun def bitbake_branch(self): 1017*4882a593Smuzhiyun self._delattr('bitbake_branch', prop=False) 1018*4882a593Smuzhiyun 1019*4882a593Smuzhiyun 1020*4882a593Smuzhiyunclass LayerItem(LayerIndexItemObj): 1021*4882a593Smuzhiyun def define_data(self, id, name, status='P', 1022*4882a593Smuzhiyun layer_type='A', summary=None, 1023*4882a593Smuzhiyun description=None, 1024*4882a593Smuzhiyun vcs_url=None, vcs_web_url=None, 1025*4882a593Smuzhiyun vcs_web_tree_base_url=None, 1026*4882a593Smuzhiyun vcs_web_file_base_url=None, 1027*4882a593Smuzhiyun usage_url=None, 1028*4882a593Smuzhiyun mailing_list_url=None, 1029*4882a593Smuzhiyun index_preference=1, 1030*4882a593Smuzhiyun classic=False, 1031*4882a593Smuzhiyun updated=None): 1032*4882a593Smuzhiyun self.id = id 1033*4882a593Smuzhiyun self.name = name 1034*4882a593Smuzhiyun self.status = status 1035*4882a593Smuzhiyun self.layer_type = layer_type 1036*4882a593Smuzhiyun self.summary = summary or name 1037*4882a593Smuzhiyun self.description = description or summary or name 1038*4882a593Smuzhiyun self.vcs_url = vcs_url 1039*4882a593Smuzhiyun self.vcs_web_url = vcs_web_url 1040*4882a593Smuzhiyun self.vcs_web_tree_base_url = vcs_web_tree_base_url 1041*4882a593Smuzhiyun self.vcs_web_file_base_url = vcs_web_file_base_url 1042*4882a593Smuzhiyun self.index_preference = index_preference 1043*4882a593Smuzhiyun self.classic = classic 1044*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 1045*4882a593Smuzhiyun 1046*4882a593Smuzhiyun 1047*4882a593Smuzhiyunclass LayerBranch(LayerIndexItemObj): 1048*4882a593Smuzhiyun def define_data(self, id, collection, version, layer, branch, 1049*4882a593Smuzhiyun vcs_subdir="", vcs_last_fetch=None, 1050*4882a593Smuzhiyun vcs_last_rev=None, vcs_last_commit=None, 1051*4882a593Smuzhiyun actual_branch="", 1052*4882a593Smuzhiyun updated=None): 1053*4882a593Smuzhiyun self.id = id 1054*4882a593Smuzhiyun self.collection = collection 1055*4882a593Smuzhiyun self.version = version 1056*4882a593Smuzhiyun if isinstance(layer, LayerItem): 1057*4882a593Smuzhiyun self.layer = layer 1058*4882a593Smuzhiyun else: 1059*4882a593Smuzhiyun self.layer_id = layer 1060*4882a593Smuzhiyun 1061*4882a593Smuzhiyun if isinstance(branch, Branch): 1062*4882a593Smuzhiyun self.branch = branch 1063*4882a593Smuzhiyun else: 1064*4882a593Smuzhiyun self.branch_id = branch 1065*4882a593Smuzhiyun 1066*4882a593Smuzhiyun self.vcs_subdir = vcs_subdir 1067*4882a593Smuzhiyun self.vcs_last_fetch = vcs_last_fetch 1068*4882a593Smuzhiyun self.vcs_last_rev = vcs_last_rev 1069*4882a593Smuzhiyun self.vcs_last_commit = vcs_last_commit 1070*4882a593Smuzhiyun self.actual_branch = actual_branch 1071*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 1072*4882a593Smuzhiyun 1073*4882a593Smuzhiyun # This is a little odd, the _data attribute is 'layer', but it's really 1074*4882a593Smuzhiyun # referring to the layer id.. so lets adjust this to make it useful 1075*4882a593Smuzhiyun @property 1076*4882a593Smuzhiyun def layer_id(self): 1077*4882a593Smuzhiyun return self.__getattr__('layer') 1078*4882a593Smuzhiyun 1079*4882a593Smuzhiyun @layer_id.setter 1080*4882a593Smuzhiyun def layer_id(self, value): 1081*4882a593Smuzhiyun self._setattr('layer', value, prop=False) 1082*4882a593Smuzhiyun 1083*4882a593Smuzhiyun @layer_id.deleter 1084*4882a593Smuzhiyun def layer_id(self): 1085*4882a593Smuzhiyun self._delattr('layer', prop=False) 1086*4882a593Smuzhiyun 1087*4882a593Smuzhiyun @property 1088*4882a593Smuzhiyun def layer(self): 1089*4882a593Smuzhiyun try: 1090*4882a593Smuzhiyun return self.index.layerItems[self.layer_id] 1091*4882a593Smuzhiyun except KeyError: 1092*4882a593Smuzhiyun raise AttributeError('Unable to find layerItems in index to map layer_id %s' % self.layer_id) 1093*4882a593Smuzhiyun except IndexError: 1094*4882a593Smuzhiyun raise AttributeError('Unable to find layer_id %s in index layerItems' % self.layer_id) 1095*4882a593Smuzhiyun 1096*4882a593Smuzhiyun @layer.setter 1097*4882a593Smuzhiyun def layer(self, value): 1098*4882a593Smuzhiyun if not isinstance(value, LayerItem): 1099*4882a593Smuzhiyun raise TypeError('value is not a LayerItem') 1100*4882a593Smuzhiyun if self.index != value.index: 1101*4882a593Smuzhiyun raise AttributeError('Object and value do not share the same index and thus key set.') 1102*4882a593Smuzhiyun self.layer_id = value.id 1103*4882a593Smuzhiyun 1104*4882a593Smuzhiyun @layer.deleter 1105*4882a593Smuzhiyun def layer(self): 1106*4882a593Smuzhiyun del self.layer_id 1107*4882a593Smuzhiyun 1108*4882a593Smuzhiyun @property 1109*4882a593Smuzhiyun def branch_id(self): 1110*4882a593Smuzhiyun return self.__getattr__('branch') 1111*4882a593Smuzhiyun 1112*4882a593Smuzhiyun @branch_id.setter 1113*4882a593Smuzhiyun def branch_id(self, value): 1114*4882a593Smuzhiyun self._setattr('branch', value, prop=False) 1115*4882a593Smuzhiyun 1116*4882a593Smuzhiyun @branch_id.deleter 1117*4882a593Smuzhiyun def branch_id(self): 1118*4882a593Smuzhiyun self._delattr('branch', prop=False) 1119*4882a593Smuzhiyun 1120*4882a593Smuzhiyun @property 1121*4882a593Smuzhiyun def branch(self): 1122*4882a593Smuzhiyun try: 1123*4882a593Smuzhiyun logger.debug("Get branch object from branches[%s]" % (self.branch_id)) 1124*4882a593Smuzhiyun return self.index.branches[self.branch_id] 1125*4882a593Smuzhiyun except KeyError: 1126*4882a593Smuzhiyun raise AttributeError('Unable to find branches in index to map branch_id %s' % self.branch_id) 1127*4882a593Smuzhiyun except IndexError: 1128*4882a593Smuzhiyun raise AttributeError('Unable to find branch_id %s in index branches' % self.branch_id) 1129*4882a593Smuzhiyun 1130*4882a593Smuzhiyun @branch.setter 1131*4882a593Smuzhiyun def branch(self, value): 1132*4882a593Smuzhiyun if not isinstance(value, LayerItem): 1133*4882a593Smuzhiyun raise TypeError('value is not a LayerItem') 1134*4882a593Smuzhiyun if self.index != value.index: 1135*4882a593Smuzhiyun raise AttributeError('Object and value do not share the same index and thus key set.') 1136*4882a593Smuzhiyun self.branch_id = value.id 1137*4882a593Smuzhiyun 1138*4882a593Smuzhiyun @branch.deleter 1139*4882a593Smuzhiyun def branch(self): 1140*4882a593Smuzhiyun del self.branch_id 1141*4882a593Smuzhiyun 1142*4882a593Smuzhiyun @property 1143*4882a593Smuzhiyun def actual_branch(self): 1144*4882a593Smuzhiyun if self.__getattr__('actual_branch'): 1145*4882a593Smuzhiyun return self.__getattr__('actual_branch') 1146*4882a593Smuzhiyun else: 1147*4882a593Smuzhiyun return self.branch.name 1148*4882a593Smuzhiyun 1149*4882a593Smuzhiyun @actual_branch.setter 1150*4882a593Smuzhiyun def actual_branch(self, value): 1151*4882a593Smuzhiyun logger.debug("Set actual_branch to %s .. name is %s" % (value, self.branch.name)) 1152*4882a593Smuzhiyun if value != self.branch.name: 1153*4882a593Smuzhiyun self._setattr('actual_branch', value, prop=False) 1154*4882a593Smuzhiyun else: 1155*4882a593Smuzhiyun self._setattr('actual_branch', '', prop=False) 1156*4882a593Smuzhiyun 1157*4882a593Smuzhiyun @actual_branch.deleter 1158*4882a593Smuzhiyun def actual_branch(self): 1159*4882a593Smuzhiyun self._delattr('actual_branch', prop=False) 1160*4882a593Smuzhiyun 1161*4882a593Smuzhiyun# Extend LayerIndexItemObj with common LayerBranch manipulations 1162*4882a593Smuzhiyun# All of the remaining LayerIndex objects refer to layerbranch, and it is 1163*4882a593Smuzhiyun# up to the user to follow that back through the LayerBranch object into 1164*4882a593Smuzhiyun# the layer object to get various attributes. So add an intermediate set 1165*4882a593Smuzhiyun# of attributes that can easily get us the layerbranch as well as layer. 1166*4882a593Smuzhiyun 1167*4882a593Smuzhiyunclass LayerIndexItemObj_LayerBranch(LayerIndexItemObj): 1168*4882a593Smuzhiyun @property 1169*4882a593Smuzhiyun def layerbranch_id(self): 1170*4882a593Smuzhiyun return self.__getattr__('layerbranch') 1171*4882a593Smuzhiyun 1172*4882a593Smuzhiyun @layerbranch_id.setter 1173*4882a593Smuzhiyun def layerbranch_id(self, value): 1174*4882a593Smuzhiyun self._setattr('layerbranch', value, prop=False) 1175*4882a593Smuzhiyun 1176*4882a593Smuzhiyun @layerbranch_id.deleter 1177*4882a593Smuzhiyun def layerbranch_id(self): 1178*4882a593Smuzhiyun self._delattr('layerbranch', prop=False) 1179*4882a593Smuzhiyun 1180*4882a593Smuzhiyun @property 1181*4882a593Smuzhiyun def layerbranch(self): 1182*4882a593Smuzhiyun try: 1183*4882a593Smuzhiyun return self.index.layerBranches[self.layerbranch_id] 1184*4882a593Smuzhiyun except KeyError: 1185*4882a593Smuzhiyun raise AttributeError('Unable to find layerBranches in index to map layerbranch_id %s' % self.layerbranch_id) 1186*4882a593Smuzhiyun except IndexError: 1187*4882a593Smuzhiyun raise AttributeError('Unable to find layerbranch_id %s in index branches' % self.layerbranch_id) 1188*4882a593Smuzhiyun 1189*4882a593Smuzhiyun @layerbranch.setter 1190*4882a593Smuzhiyun def layerbranch(self, value): 1191*4882a593Smuzhiyun if not isinstance(value, LayerBranch): 1192*4882a593Smuzhiyun raise TypeError('value (%s) is not a layerBranch' % type(value)) 1193*4882a593Smuzhiyun if self.index != value.index: 1194*4882a593Smuzhiyun raise AttributeError('Object and value do not share the same index and thus key set.') 1195*4882a593Smuzhiyun self.layerbranch_id = value.id 1196*4882a593Smuzhiyun 1197*4882a593Smuzhiyun @layerbranch.deleter 1198*4882a593Smuzhiyun def layerbranch(self): 1199*4882a593Smuzhiyun del self.layerbranch_id 1200*4882a593Smuzhiyun 1201*4882a593Smuzhiyun @property 1202*4882a593Smuzhiyun def layer_id(self): 1203*4882a593Smuzhiyun return self.layerbranch.layer_id 1204*4882a593Smuzhiyun 1205*4882a593Smuzhiyun # Doesn't make sense to set or delete layer_id 1206*4882a593Smuzhiyun 1207*4882a593Smuzhiyun @property 1208*4882a593Smuzhiyun def layer(self): 1209*4882a593Smuzhiyun return self.layerbranch.layer 1210*4882a593Smuzhiyun 1211*4882a593Smuzhiyun # Doesn't make sense to set or delete layer 1212*4882a593Smuzhiyun 1213*4882a593Smuzhiyun 1214*4882a593Smuzhiyunclass LayerDependency(LayerIndexItemObj_LayerBranch): 1215*4882a593Smuzhiyun def define_data(self, id, layerbranch, dependency, required=True): 1216*4882a593Smuzhiyun self.id = id 1217*4882a593Smuzhiyun if isinstance(layerbranch, LayerBranch): 1218*4882a593Smuzhiyun self.layerbranch = layerbranch 1219*4882a593Smuzhiyun else: 1220*4882a593Smuzhiyun self.layerbranch_id = layerbranch 1221*4882a593Smuzhiyun if isinstance(dependency, LayerDependency): 1222*4882a593Smuzhiyun self.dependency = dependency 1223*4882a593Smuzhiyun else: 1224*4882a593Smuzhiyun self.dependency_id = dependency 1225*4882a593Smuzhiyun self.required = required 1226*4882a593Smuzhiyun 1227*4882a593Smuzhiyun @property 1228*4882a593Smuzhiyun def dependency_id(self): 1229*4882a593Smuzhiyun return self.__getattr__('dependency') 1230*4882a593Smuzhiyun 1231*4882a593Smuzhiyun @dependency_id.setter 1232*4882a593Smuzhiyun def dependency_id(self, value): 1233*4882a593Smuzhiyun self._setattr('dependency', value, prop=False) 1234*4882a593Smuzhiyun 1235*4882a593Smuzhiyun @dependency_id.deleter 1236*4882a593Smuzhiyun def dependency_id(self): 1237*4882a593Smuzhiyun self._delattr('dependency', prop=False) 1238*4882a593Smuzhiyun 1239*4882a593Smuzhiyun @property 1240*4882a593Smuzhiyun def dependency(self): 1241*4882a593Smuzhiyun try: 1242*4882a593Smuzhiyun return self.index.layerItems[self.dependency_id] 1243*4882a593Smuzhiyun except KeyError: 1244*4882a593Smuzhiyun raise AttributeError('Unable to find layerItems in index to map layerbranch_id %s' % self.dependency_id) 1245*4882a593Smuzhiyun except IndexError: 1246*4882a593Smuzhiyun raise AttributeError('Unable to find dependency_id %s in index layerItems' % self.dependency_id) 1247*4882a593Smuzhiyun 1248*4882a593Smuzhiyun @dependency.setter 1249*4882a593Smuzhiyun def dependency(self, value): 1250*4882a593Smuzhiyun if not isinstance(value, LayerDependency): 1251*4882a593Smuzhiyun raise TypeError('value (%s) is not a dependency' % type(value)) 1252*4882a593Smuzhiyun if self.index != value.index: 1253*4882a593Smuzhiyun raise AttributeError('Object and value do not share the same index and thus key set.') 1254*4882a593Smuzhiyun self.dependency_id = value.id 1255*4882a593Smuzhiyun 1256*4882a593Smuzhiyun @dependency.deleter 1257*4882a593Smuzhiyun def dependency(self): 1258*4882a593Smuzhiyun self._delattr('dependency', prop=False) 1259*4882a593Smuzhiyun 1260*4882a593Smuzhiyun @property 1261*4882a593Smuzhiyun def dependency_layerBranch(self): 1262*4882a593Smuzhiyun layerid = self.dependency_id 1263*4882a593Smuzhiyun branchid = self.layerbranch.branch_id 1264*4882a593Smuzhiyun 1265*4882a593Smuzhiyun try: 1266*4882a593Smuzhiyun return self.index.layerBranches_layerId_branchId["%s:%s" % (layerid, branchid)] 1267*4882a593Smuzhiyun except IndexError: 1268*4882a593Smuzhiyun # layerBranches_layerId_branchId -- but not layerId:branchId 1269*4882a593Smuzhiyun raise AttributeError('Unable to find layerId:branchId %s:%s in index layerBranches_layerId_branchId' % (layerid, branchid)) 1270*4882a593Smuzhiyun except KeyError: 1271*4882a593Smuzhiyun raise AttributeError('Unable to find layerId:branchId %s:%s in layerItems and layerBranches' % (layerid, branchid)) 1272*4882a593Smuzhiyun 1273*4882a593Smuzhiyun # dependency_layerBranch doesn't make sense to set or del 1274*4882a593Smuzhiyun 1275*4882a593Smuzhiyun 1276*4882a593Smuzhiyunclass Recipe(LayerIndexItemObj_LayerBranch): 1277*4882a593Smuzhiyun def define_data(self, id, 1278*4882a593Smuzhiyun filename, filepath, pn, pv, layerbranch, 1279*4882a593Smuzhiyun summary="", description="", section="", license="", 1280*4882a593Smuzhiyun homepage="", bugtracker="", provides="", bbclassextend="", 1281*4882a593Smuzhiyun inherits="", disallowed="", updated=None): 1282*4882a593Smuzhiyun self.id = id 1283*4882a593Smuzhiyun self.filename = filename 1284*4882a593Smuzhiyun self.filepath = filepath 1285*4882a593Smuzhiyun self.pn = pn 1286*4882a593Smuzhiyun self.pv = pv 1287*4882a593Smuzhiyun self.summary = summary 1288*4882a593Smuzhiyun self.description = description 1289*4882a593Smuzhiyun self.section = section 1290*4882a593Smuzhiyun self.license = license 1291*4882a593Smuzhiyun self.homepage = homepage 1292*4882a593Smuzhiyun self.bugtracker = bugtracker 1293*4882a593Smuzhiyun self.provides = provides 1294*4882a593Smuzhiyun self.bbclassextend = bbclassextend 1295*4882a593Smuzhiyun self.inherits = inherits 1296*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 1297*4882a593Smuzhiyun self.disallowed = disallowed 1298*4882a593Smuzhiyun if isinstance(layerbranch, LayerBranch): 1299*4882a593Smuzhiyun self.layerbranch = layerbranch 1300*4882a593Smuzhiyun else: 1301*4882a593Smuzhiyun self.layerbranch_id = layerbranch 1302*4882a593Smuzhiyun 1303*4882a593Smuzhiyun @property 1304*4882a593Smuzhiyun def fullpath(self): 1305*4882a593Smuzhiyun return os.path.join(self.filepath, self.filename) 1306*4882a593Smuzhiyun 1307*4882a593Smuzhiyun # Set would need to understand how to split it 1308*4882a593Smuzhiyun # del would we del both parts? 1309*4882a593Smuzhiyun 1310*4882a593Smuzhiyun @property 1311*4882a593Smuzhiyun def inherits(self): 1312*4882a593Smuzhiyun if 'inherits' not in self._data: 1313*4882a593Smuzhiyun # Older indexes may not have this, so emulate it 1314*4882a593Smuzhiyun if '-image-' in self.pn: 1315*4882a593Smuzhiyun return 'image' 1316*4882a593Smuzhiyun return self.__getattr__('inherits') 1317*4882a593Smuzhiyun 1318*4882a593Smuzhiyun @inherits.setter 1319*4882a593Smuzhiyun def inherits(self, value): 1320*4882a593Smuzhiyun return self._setattr('inherits', value, prop=False) 1321*4882a593Smuzhiyun 1322*4882a593Smuzhiyun @inherits.deleter 1323*4882a593Smuzhiyun def inherits(self): 1324*4882a593Smuzhiyun return self._delattr('inherits', prop=False) 1325*4882a593Smuzhiyun 1326*4882a593Smuzhiyun 1327*4882a593Smuzhiyunclass Machine(LayerIndexItemObj_LayerBranch): 1328*4882a593Smuzhiyun def define_data(self, id, 1329*4882a593Smuzhiyun name, description, layerbranch, 1330*4882a593Smuzhiyun updated=None): 1331*4882a593Smuzhiyun self.id = id 1332*4882a593Smuzhiyun self.name = name 1333*4882a593Smuzhiyun self.description = description 1334*4882a593Smuzhiyun if isinstance(layerbranch, LayerBranch): 1335*4882a593Smuzhiyun self.layerbranch = layerbranch 1336*4882a593Smuzhiyun else: 1337*4882a593Smuzhiyun self.layerbranch_id = layerbranch 1338*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 1339*4882a593Smuzhiyun 1340*4882a593Smuzhiyunclass Distro(LayerIndexItemObj_LayerBranch): 1341*4882a593Smuzhiyun def define_data(self, id, 1342*4882a593Smuzhiyun name, description, layerbranch, 1343*4882a593Smuzhiyun updated=None): 1344*4882a593Smuzhiyun self.id = id 1345*4882a593Smuzhiyun self.name = name 1346*4882a593Smuzhiyun self.description = description 1347*4882a593Smuzhiyun if isinstance(layerbranch, LayerBranch): 1348*4882a593Smuzhiyun self.layerbranch = layerbranch 1349*4882a593Smuzhiyun else: 1350*4882a593Smuzhiyun self.layerbranch_id = layerbranch 1351*4882a593Smuzhiyun self.updated = updated or datetime.datetime.today().isoformat() 1352*4882a593Smuzhiyun 1353*4882a593Smuzhiyun# When performing certain actions, we may need to sort the data. 1354*4882a593Smuzhiyun# This will allow us to keep it consistent from run to run. 1355*4882a593Smuzhiyundef sort_entry(item): 1356*4882a593Smuzhiyun newitem = item 1357*4882a593Smuzhiyun try: 1358*4882a593Smuzhiyun if type(newitem) == type(dict()): 1359*4882a593Smuzhiyun newitem = OrderedDict(sorted(newitem.items(), key=lambda t: t[0])) 1360*4882a593Smuzhiyun for index in newitem: 1361*4882a593Smuzhiyun newitem[index] = sort_entry(newitem[index]) 1362*4882a593Smuzhiyun elif type(newitem) == type(list()): 1363*4882a593Smuzhiyun newitem.sort(key=lambda obj: obj['id']) 1364*4882a593Smuzhiyun for index, _ in enumerate(newitem): 1365*4882a593Smuzhiyun newitem[index] = sort_entry(newitem[index]) 1366*4882a593Smuzhiyun except: 1367*4882a593Smuzhiyun logger.error('Sort failed for item %s' % type(item)) 1368*4882a593Smuzhiyun pass 1369*4882a593Smuzhiyun 1370*4882a593Smuzhiyun return newitem 1371