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