1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# This class should provide easy access to the different aspects of the 5*4882a593Smuzhiyun# buildsystem such as layers, bitbake location, etc. 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# SDK_LAYERS_EXCLUDE: Layers which will be excluded from SDK layers. 8*4882a593Smuzhiyun# SDK_LAYERS_EXCLUDE_PATTERN: The simiar to SDK_LAYERS_EXCLUDE, this supports 9*4882a593Smuzhiyun# python regular expression, use space as separator, 10*4882a593Smuzhiyun# e.g.: ".*-downloads closed-.*" 11*4882a593Smuzhiyun# 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport stat 14*4882a593Smuzhiyunimport shutil 15*4882a593Smuzhiyun 16*4882a593Smuzhiyundef _smart_copy(src, dest): 17*4882a593Smuzhiyun import subprocess 18*4882a593Smuzhiyun # smart_copy will choose the correct function depending on whether the 19*4882a593Smuzhiyun # source is a file or a directory. 20*4882a593Smuzhiyun mode = os.stat(src).st_mode 21*4882a593Smuzhiyun if stat.S_ISDIR(mode): 22*4882a593Smuzhiyun bb.utils.mkdirhier(dest) 23*4882a593Smuzhiyun cmd = "tar --exclude='.git' --exclude='__pycache__' --xattrs --xattrs-include='*' -chf - -C %s -p . \ 24*4882a593Smuzhiyun | tar --xattrs --xattrs-include='*' -xf - -C %s" % (src, dest) 25*4882a593Smuzhiyun subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) 26*4882a593Smuzhiyun else: 27*4882a593Smuzhiyun shutil.copyfile(src, dest) 28*4882a593Smuzhiyun shutil.copymode(src, dest) 29*4882a593Smuzhiyun 30*4882a593Smuzhiyunclass BuildSystem(object): 31*4882a593Smuzhiyun def __init__(self, context, d): 32*4882a593Smuzhiyun self.d = d 33*4882a593Smuzhiyun self.context = context 34*4882a593Smuzhiyun self.layerdirs = [os.path.abspath(pth) for pth in d.getVar('BBLAYERS').split()] 35*4882a593Smuzhiyun self.layers_exclude = (d.getVar('SDK_LAYERS_EXCLUDE') or "").split() 36*4882a593Smuzhiyun self.layers_exclude_pattern = d.getVar('SDK_LAYERS_EXCLUDE_PATTERN') 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun def copy_bitbake_and_layers(self, destdir, workspace_name=None): 39*4882a593Smuzhiyun import re 40*4882a593Smuzhiyun # Copy in all metadata layers + bitbake (as repositories) 41*4882a593Smuzhiyun copied_corebase = None 42*4882a593Smuzhiyun layers_copied = [] 43*4882a593Smuzhiyun bb.utils.mkdirhier(destdir) 44*4882a593Smuzhiyun layers = list(self.layerdirs) 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun corebase = os.path.abspath(self.d.getVar('COREBASE')) 47*4882a593Smuzhiyun layers.append(corebase) 48*4882a593Smuzhiyun # The bitbake build system uses the meta-skeleton layer as a layout 49*4882a593Smuzhiyun # for common recipies, e.g: the recipetool script to create kernel recipies 50*4882a593Smuzhiyun # Add the meta-skeleton layer to be included as part of the eSDK installation 51*4882a593Smuzhiyun layers.append(os.path.join(corebase, 'meta-skeleton')) 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun # Exclude layers 54*4882a593Smuzhiyun for layer_exclude in self.layers_exclude: 55*4882a593Smuzhiyun if layer_exclude in layers: 56*4882a593Smuzhiyun bb.note('Excluded %s from sdk layers since it is in SDK_LAYERS_EXCLUDE' % layer_exclude) 57*4882a593Smuzhiyun layers.remove(layer_exclude) 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun if self.layers_exclude_pattern: 60*4882a593Smuzhiyun layers_cp = layers[:] 61*4882a593Smuzhiyun for pattern in self.layers_exclude_pattern.split(): 62*4882a593Smuzhiyun for layer in layers_cp: 63*4882a593Smuzhiyun if re.match(pattern, layer): 64*4882a593Smuzhiyun bb.note('Excluded %s from sdk layers since matched SDK_LAYERS_EXCLUDE_PATTERN' % layer) 65*4882a593Smuzhiyun layers.remove(layer) 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun workspace_newname = workspace_name 68*4882a593Smuzhiyun if workspace_newname: 69*4882a593Smuzhiyun layernames = [os.path.basename(layer) for layer in layers] 70*4882a593Smuzhiyun extranum = 0 71*4882a593Smuzhiyun while workspace_newname in layernames: 72*4882a593Smuzhiyun extranum += 1 73*4882a593Smuzhiyun workspace_newname = '%s-%d' % (workspace_name, extranum) 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun corebase_files = self.d.getVar('COREBASE_FILES').split() 76*4882a593Smuzhiyun corebase_files = [corebase + '/' +x for x in corebase_files] 77*4882a593Smuzhiyun # Make sure bitbake goes in 78*4882a593Smuzhiyun bitbake_dir = bb.__file__.rsplit('/', 3)[0] 79*4882a593Smuzhiyun corebase_files.append(bitbake_dir) 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun for layer in layers: 82*4882a593Smuzhiyun layerconf = os.path.join(layer, 'conf', 'layer.conf') 83*4882a593Smuzhiyun layernewname = os.path.basename(layer) 84*4882a593Smuzhiyun workspace = False 85*4882a593Smuzhiyun if os.path.exists(layerconf): 86*4882a593Smuzhiyun with open(layerconf, 'r') as f: 87*4882a593Smuzhiyun if f.readline().startswith("# ### workspace layer auto-generated by devtool ###"): 88*4882a593Smuzhiyun if workspace_newname: 89*4882a593Smuzhiyun layernewname = workspace_newname 90*4882a593Smuzhiyun workspace = True 91*4882a593Smuzhiyun else: 92*4882a593Smuzhiyun bb.plain("NOTE: Excluding local workspace layer %s from %s" % (layer, self.context)) 93*4882a593Smuzhiyun continue 94*4882a593Smuzhiyun 95*4882a593Smuzhiyun # If the layer was already under corebase, leave it there 96*4882a593Smuzhiyun # since layers such as meta have issues when moved. 97*4882a593Smuzhiyun layerdestpath = destdir 98*4882a593Smuzhiyun if corebase == os.path.dirname(layer): 99*4882a593Smuzhiyun layerdestpath += '/' + os.path.basename(corebase) 100*4882a593Smuzhiyun # If the layer is located somewhere under the same parent directory 101*4882a593Smuzhiyun # as corebase we keep the layer structure. 102*4882a593Smuzhiyun elif os.path.commonpath([layer, corebase]) == os.path.dirname(corebase): 103*4882a593Smuzhiyun layer_relative = os.path.relpath(layer, os.path.dirname(corebase)) 104*4882a593Smuzhiyun if os.path.dirname(layer_relative) != layernewname: 105*4882a593Smuzhiyun layerdestpath += '/' + os.path.dirname(layer_relative) 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun layerdestpath += '/' + layernewname 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun layer_relative = os.path.relpath(layerdestpath, 110*4882a593Smuzhiyun destdir) 111*4882a593Smuzhiyun # Treat corebase as special since it typically will contain 112*4882a593Smuzhiyun # build directories or other custom items. 113*4882a593Smuzhiyun if corebase == layer: 114*4882a593Smuzhiyun copied_corebase = layer_relative 115*4882a593Smuzhiyun bb.utils.mkdirhier(layerdestpath) 116*4882a593Smuzhiyun for f in corebase_files: 117*4882a593Smuzhiyun f_basename = os.path.basename(f) 118*4882a593Smuzhiyun destname = os.path.join(layerdestpath, f_basename) 119*4882a593Smuzhiyun _smart_copy(f, destname) 120*4882a593Smuzhiyun else: 121*4882a593Smuzhiyun layers_copied.append(layer_relative) 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun if os.path.exists(os.path.join(layerdestpath, 'conf/layer.conf')): 124*4882a593Smuzhiyun bb.note("Skipping layer %s, already handled" % layer) 125*4882a593Smuzhiyun else: 126*4882a593Smuzhiyun _smart_copy(layer, layerdestpath) 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun if workspace: 129*4882a593Smuzhiyun # Make some adjustments original workspace layer 130*4882a593Smuzhiyun # Drop sources (recipe tasks will be locked, so we don't need them) 131*4882a593Smuzhiyun srcdir = os.path.join(layerdestpath, 'sources') 132*4882a593Smuzhiyun if os.path.isdir(srcdir): 133*4882a593Smuzhiyun shutil.rmtree(srcdir) 134*4882a593Smuzhiyun # Drop all bbappends except the one for the image the SDK is being built for 135*4882a593Smuzhiyun # (because of externalsrc, the workspace bbappends will interfere with the 136*4882a593Smuzhiyun # locked signatures if present, and we don't need them anyway) 137*4882a593Smuzhiyun image_bbappend = os.path.splitext(os.path.basename(self.d.getVar('FILE')))[0] + '.bbappend' 138*4882a593Smuzhiyun appenddir = os.path.join(layerdestpath, 'appends') 139*4882a593Smuzhiyun if os.path.isdir(appenddir): 140*4882a593Smuzhiyun for fn in os.listdir(appenddir): 141*4882a593Smuzhiyun if fn == image_bbappend: 142*4882a593Smuzhiyun continue 143*4882a593Smuzhiyun else: 144*4882a593Smuzhiyun os.remove(os.path.join(appenddir, fn)) 145*4882a593Smuzhiyun # Drop README 146*4882a593Smuzhiyun readme = os.path.join(layerdestpath, 'README') 147*4882a593Smuzhiyun if os.path.exists(readme): 148*4882a593Smuzhiyun os.remove(readme) 149*4882a593Smuzhiyun # Filter out comments in layer.conf and change layer name 150*4882a593Smuzhiyun layerconf = os.path.join(layerdestpath, 'conf', 'layer.conf') 151*4882a593Smuzhiyun with open(layerconf, 'r') as f: 152*4882a593Smuzhiyun origlines = f.readlines() 153*4882a593Smuzhiyun with open(layerconf, 'w') as f: 154*4882a593Smuzhiyun for line in origlines: 155*4882a593Smuzhiyun if line.startswith('#'): 156*4882a593Smuzhiyun continue 157*4882a593Smuzhiyun line = line.replace('workspacelayer', workspace_newname) 158*4882a593Smuzhiyun f.write(line) 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun # meta-skeleton layer is added as part of the build system 161*4882a593Smuzhiyun # but not as a layer included in the build, therefore it is 162*4882a593Smuzhiyun # not reported to the function caller. 163*4882a593Smuzhiyun for layer in layers_copied: 164*4882a593Smuzhiyun if layer.endswith('/meta-skeleton'): 165*4882a593Smuzhiyun layers_copied.remove(layer) 166*4882a593Smuzhiyun break 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun return copied_corebase, layers_copied 169*4882a593Smuzhiyun 170*4882a593Smuzhiyundef generate_locked_sigs(sigfile, d): 171*4882a593Smuzhiyun bb.utils.mkdirhier(os.path.dirname(sigfile)) 172*4882a593Smuzhiyun depd = d.getVar('BB_TASKDEPDATA', False) 173*4882a593Smuzhiyun tasks = ['%s:%s' % (v[2], v[1]) for v in depd.values()] 174*4882a593Smuzhiyun bb.parse.siggen.dump_lockedsigs(sigfile, tasks) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyundef prune_lockedsigs(excluded_tasks, excluded_targets, lockedsigs, onlynative, pruned_output): 177*4882a593Smuzhiyun with open(lockedsigs, 'r') as infile: 178*4882a593Smuzhiyun bb.utils.mkdirhier(os.path.dirname(pruned_output)) 179*4882a593Smuzhiyun with open(pruned_output, 'w') as f: 180*4882a593Smuzhiyun invalue = False 181*4882a593Smuzhiyun for line in infile: 182*4882a593Smuzhiyun if invalue: 183*4882a593Smuzhiyun if line.endswith('\\\n'): 184*4882a593Smuzhiyun splitval = line.strip().split(':') 185*4882a593Smuzhiyun if not splitval[1] in excluded_tasks and not splitval[0] in excluded_targets: 186*4882a593Smuzhiyun if onlynative: 187*4882a593Smuzhiyun if 'nativesdk' in splitval[0]: 188*4882a593Smuzhiyun f.write(line) 189*4882a593Smuzhiyun else: 190*4882a593Smuzhiyun f.write(line) 191*4882a593Smuzhiyun else: 192*4882a593Smuzhiyun f.write(line) 193*4882a593Smuzhiyun invalue = False 194*4882a593Smuzhiyun elif line.startswith('SIGGEN_LOCKEDSIGS'): 195*4882a593Smuzhiyun invalue = True 196*4882a593Smuzhiyun f.write(line) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyundef merge_lockedsigs(copy_tasks, lockedsigs_main, lockedsigs_extra, merged_output, copy_output=None): 199*4882a593Smuzhiyun merged = {} 200*4882a593Smuzhiyun arch_order = [] 201*4882a593Smuzhiyun with open(lockedsigs_main, 'r') as f: 202*4882a593Smuzhiyun invalue = None 203*4882a593Smuzhiyun for line in f: 204*4882a593Smuzhiyun if invalue: 205*4882a593Smuzhiyun if line.endswith('\\\n'): 206*4882a593Smuzhiyun merged[invalue].append(line) 207*4882a593Smuzhiyun else: 208*4882a593Smuzhiyun invalue = None 209*4882a593Smuzhiyun elif line.startswith('SIGGEN_LOCKEDSIGS_t-'): 210*4882a593Smuzhiyun invalue = line[18:].split('=', 1)[0].rstrip() 211*4882a593Smuzhiyun merged[invalue] = [] 212*4882a593Smuzhiyun arch_order.append(invalue) 213*4882a593Smuzhiyun 214*4882a593Smuzhiyun with open(lockedsigs_extra, 'r') as f: 215*4882a593Smuzhiyun invalue = None 216*4882a593Smuzhiyun tocopy = {} 217*4882a593Smuzhiyun for line in f: 218*4882a593Smuzhiyun if invalue: 219*4882a593Smuzhiyun if line.endswith('\\\n'): 220*4882a593Smuzhiyun if not line in merged[invalue]: 221*4882a593Smuzhiyun target, task = line.strip().split(':')[:2] 222*4882a593Smuzhiyun if not copy_tasks or task in copy_tasks: 223*4882a593Smuzhiyun tocopy[invalue].append(line) 224*4882a593Smuzhiyun merged[invalue].append(line) 225*4882a593Smuzhiyun else: 226*4882a593Smuzhiyun invalue = None 227*4882a593Smuzhiyun elif line.startswith('SIGGEN_LOCKEDSIGS_t-'): 228*4882a593Smuzhiyun invalue = line[18:].split('=', 1)[0].rstrip() 229*4882a593Smuzhiyun if not invalue in merged: 230*4882a593Smuzhiyun merged[invalue] = [] 231*4882a593Smuzhiyun arch_order.append(invalue) 232*4882a593Smuzhiyun tocopy[invalue] = [] 233*4882a593Smuzhiyun 234*4882a593Smuzhiyun def write_sigs_file(fn, types, sigs): 235*4882a593Smuzhiyun fulltypes = [] 236*4882a593Smuzhiyun bb.utils.mkdirhier(os.path.dirname(fn)) 237*4882a593Smuzhiyun with open(fn, 'w') as f: 238*4882a593Smuzhiyun for typename in types: 239*4882a593Smuzhiyun lines = sigs[typename] 240*4882a593Smuzhiyun if lines: 241*4882a593Smuzhiyun f.write('SIGGEN_LOCKEDSIGS_%s = "\\\n' % typename) 242*4882a593Smuzhiyun for line in lines: 243*4882a593Smuzhiyun f.write(line) 244*4882a593Smuzhiyun f.write(' "\n') 245*4882a593Smuzhiyun fulltypes.append(typename) 246*4882a593Smuzhiyun f.write('SIGGEN_LOCKEDSIGS_TYPES = "%s"\n' % ' '.join(fulltypes)) 247*4882a593Smuzhiyun 248*4882a593Smuzhiyun if copy_output: 249*4882a593Smuzhiyun write_sigs_file(copy_output, list(tocopy.keys()), tocopy) 250*4882a593Smuzhiyun if merged_output: 251*4882a593Smuzhiyun write_sigs_file(merged_output, arch_order, merged) 252*4882a593Smuzhiyun 253*4882a593Smuzhiyundef create_locked_sstate_cache(lockedsigs, input_sstate_cache, output_sstate_cache, d, fixedlsbstring="", filterfile=None): 254*4882a593Smuzhiyun import shutil 255*4882a593Smuzhiyun bb.note('Generating sstate-cache...') 256*4882a593Smuzhiyun 257*4882a593Smuzhiyun nativelsbstring = d.getVar('NATIVELSBSTRING') 258*4882a593Smuzhiyun bb.process.run("PYTHONDONTWRITEBYTECODE=1 gen-lockedsig-cache %s %s %s %s %s" % (lockedsigs, input_sstate_cache, output_sstate_cache, nativelsbstring, filterfile or '')) 259*4882a593Smuzhiyun if fixedlsbstring and nativelsbstring != fixedlsbstring: 260*4882a593Smuzhiyun nativedir = output_sstate_cache + '/' + nativelsbstring 261*4882a593Smuzhiyun if os.path.isdir(nativedir): 262*4882a593Smuzhiyun destdir = os.path.join(output_sstate_cache, fixedlsbstring) 263*4882a593Smuzhiyun for root, _, files in os.walk(nativedir): 264*4882a593Smuzhiyun for fn in files: 265*4882a593Smuzhiyun src = os.path.join(root, fn) 266*4882a593Smuzhiyun dest = os.path.join(destdir, os.path.relpath(src, nativedir)) 267*4882a593Smuzhiyun if os.path.exists(dest): 268*4882a593Smuzhiyun # Already exists, and it'll be the same file, so just delete it 269*4882a593Smuzhiyun os.unlink(src) 270*4882a593Smuzhiyun else: 271*4882a593Smuzhiyun bb.utils.mkdirhier(os.path.dirname(dest)) 272*4882a593Smuzhiyun shutil.move(src, dest) 273*4882a593Smuzhiyun 274*4882a593Smuzhiyundef check_sstate_task_list(d, targets, filteroutfile, cmdprefix='', cwd=None, logfile=None): 275*4882a593Smuzhiyun import subprocess 276*4882a593Smuzhiyun 277*4882a593Smuzhiyun bb.note('Generating sstate task list...') 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun if not cwd: 280*4882a593Smuzhiyun cwd = os.getcwd() 281*4882a593Smuzhiyun if logfile: 282*4882a593Smuzhiyun logparam = '-l %s' % logfile 283*4882a593Smuzhiyun else: 284*4882a593Smuzhiyun logparam = '' 285*4882a593Smuzhiyun cmd = "%sPYTHONDONTWRITEBYTECODE=1 BB_SETSCENE_ENFORCE=1 PSEUDO_DISABLED=1 oe-check-sstate %s -s -o %s %s" % (cmdprefix, targets, filteroutfile, logparam) 286*4882a593Smuzhiyun env = dict(d.getVar('BB_ORIGENV', False)) 287*4882a593Smuzhiyun env.pop('BUILDDIR', '') 288*4882a593Smuzhiyun env.pop('BBPATH', '') 289*4882a593Smuzhiyun pathitems = env['PATH'].split(':') 290*4882a593Smuzhiyun env['PATH'] = ':'.join([item for item in pathitems if not item.endswith('/bitbake/bin')]) 291*4882a593Smuzhiyun bb.process.run(cmd, stderr=subprocess.STDOUT, env=env, cwd=cwd, executable='/bin/bash') 292