xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oe/copy_buildsystem.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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