xref: /OK3568_Linux_fs/yocto/poky/meta/classes/devtool-source.bbclass (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# Development tool - source extraction helper class
2*4882a593Smuzhiyun#
3*4882a593Smuzhiyun# NOTE: this class is intended for use by devtool and should not be
4*4882a593Smuzhiyun# inherited manually.
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun# Copyright (C) 2014-2017 Intel Corporation
7*4882a593Smuzhiyun#
8*4882a593Smuzhiyun# This program is free software; you can redistribute it and/or modify
9*4882a593Smuzhiyun# it under the terms of the GNU General Public License version 2 as
10*4882a593Smuzhiyun# published by the Free Software Foundation.
11*4882a593Smuzhiyun#
12*4882a593Smuzhiyun# This program is distributed in the hope that it will be useful,
13*4882a593Smuzhiyun# but WITHOUT ANY WARRANTY; without even the implied warranty of
14*4882a593Smuzhiyun# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*4882a593Smuzhiyun# GNU General Public License for more details.
16*4882a593Smuzhiyun#
17*4882a593Smuzhiyun# You should have received a copy of the GNU General Public License along
18*4882a593Smuzhiyun# with this program; if not, write to the Free Software Foundation, Inc.,
19*4882a593Smuzhiyun# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun
22*4882a593SmuzhiyunDEVTOOL_TEMPDIR ?= ""
23*4882a593SmuzhiyunDEVTOOL_PATCH_SRCDIR = "${DEVTOOL_TEMPDIR}/patchworkdir"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun
26*4882a593Smuzhiyunpython() {
27*4882a593Smuzhiyun    tempdir = d.getVar('DEVTOOL_TEMPDIR')
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun    if not tempdir:
30*4882a593Smuzhiyun        bb.fatal('devtool-source class is for internal use by devtool only')
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun    # Make a subdir so we guard against WORKDIR==S
33*4882a593Smuzhiyun    workdir = os.path.join(tempdir, 'workdir')
34*4882a593Smuzhiyun    d.setVar('WORKDIR', workdir)
35*4882a593Smuzhiyun    if not d.getVar('S').startswith(workdir):
36*4882a593Smuzhiyun        # Usually a shared workdir recipe (kernel, gcc)
37*4882a593Smuzhiyun        # Try to set a reasonable default
38*4882a593Smuzhiyun        if bb.data.inherits_class('kernel', d):
39*4882a593Smuzhiyun            d.setVar('S', '${WORKDIR}/source')
40*4882a593Smuzhiyun        else:
41*4882a593Smuzhiyun            d.setVar('S', '${WORKDIR}/%s' % os.path.basename(d.getVar('S')))
42*4882a593Smuzhiyun    if bb.data.inherits_class('kernel', d):
43*4882a593Smuzhiyun        # We don't want to move the source to STAGING_KERNEL_DIR here
44*4882a593Smuzhiyun        d.setVar('STAGING_KERNEL_DIR', '${S}')
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun    d.setVar('STAMPS_DIR', os.path.join(tempdir, 'stamps'))
47*4882a593Smuzhiyun    d.setVar('T', os.path.join(tempdir, 'temp'))
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun    # Hook in pre/postfuncs
50*4882a593Smuzhiyun    is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
51*4882a593Smuzhiyun    if is_kernel_yocto:
52*4882a593Smuzhiyun        unpacktask = 'do_kernel_checkout'
53*4882a593Smuzhiyun        d.appendVarFlag('do_configure', 'postfuncs', ' devtool_post_configure')
54*4882a593Smuzhiyun    else:
55*4882a593Smuzhiyun        unpacktask = 'do_unpack'
56*4882a593Smuzhiyun    d.appendVarFlag(unpacktask, 'postfuncs', ' devtool_post_unpack')
57*4882a593Smuzhiyun    d.prependVarFlag('do_patch', 'prefuncs', ' devtool_pre_patch')
58*4882a593Smuzhiyun    d.appendVarFlag('do_patch', 'postfuncs', ' devtool_post_patch')
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun    # NOTE: in order for the patch stuff to be fully functional,
61*4882a593Smuzhiyun    # PATCHTOOL and PATCH_COMMIT_FUNCTIONS need to be set; we can't
62*4882a593Smuzhiyun    # do that here because we can't guarantee the order of the anonymous
63*4882a593Smuzhiyun    # functions, so it gets done in the bbappend we create.
64*4882a593Smuzhiyun}
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun
67*4882a593Smuzhiyunpython devtool_post_unpack() {
68*4882a593Smuzhiyun    import oe.recipeutils
69*4882a593Smuzhiyun    import shutil
70*4882a593Smuzhiyun    sys.path.insert(0, os.path.join(d.getVar('COREBASE'), 'scripts', 'lib'))
71*4882a593Smuzhiyun    import scriptutils
72*4882a593Smuzhiyun    from devtool import setup_git_repo
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun    tempdir = d.getVar('DEVTOOL_TEMPDIR')
75*4882a593Smuzhiyun    workdir = d.getVar('WORKDIR')
76*4882a593Smuzhiyun    srcsubdir = d.getVar('S')
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun    def _move_file(src, dst):
79*4882a593Smuzhiyun        """Move a file. Creates all the directory components of destination path."""
80*4882a593Smuzhiyun        dst_d = os.path.dirname(dst)
81*4882a593Smuzhiyun        if dst_d:
82*4882a593Smuzhiyun            bb.utils.mkdirhier(dst_d)
83*4882a593Smuzhiyun        shutil.move(src, dst)
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun    def _ls_tree(directory):
86*4882a593Smuzhiyun        """Recursive listing of files in a directory"""
87*4882a593Smuzhiyun        ret = []
88*4882a593Smuzhiyun        for root, dirs, files in os.walk(directory):
89*4882a593Smuzhiyun            ret.extend([os.path.relpath(os.path.join(root, fname), directory) for
90*4882a593Smuzhiyun                        fname in files])
91*4882a593Smuzhiyun        return ret
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun    is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
94*4882a593Smuzhiyun    # Move local source files into separate subdir
95*4882a593Smuzhiyun    recipe_patches = [os.path.basename(patch) for patch in
96*4882a593Smuzhiyun                        oe.recipeutils.get_recipe_patches(d)]
97*4882a593Smuzhiyun    local_files = oe.recipeutils.get_recipe_local_files(d)
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun    if is_kernel_yocto:
100*4882a593Smuzhiyun        for key in [f for f in local_files if f.endswith('scc')]:
101*4882a593Smuzhiyun            with open(local_files[key], 'r') as sccfile:
102*4882a593Smuzhiyun                for l in sccfile:
103*4882a593Smuzhiyun                    line = l.split()
104*4882a593Smuzhiyun                    if line and line[0] in ('kconf', 'patch'):
105*4882a593Smuzhiyun                        cfg = os.path.join(os.path.dirname(local_files[key]), line[-1])
106*4882a593Smuzhiyun                        if cfg not in local_files.values():
107*4882a593Smuzhiyun                            local_files[line[-1]] = cfg
108*4882a593Smuzhiyun                            shutil.copy2(cfg, workdir)
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun    # Ignore local files with subdir={BP}
111*4882a593Smuzhiyun    srcabspath = os.path.abspath(srcsubdir)
112*4882a593Smuzhiyun    local_files = [fname for fname in local_files if
113*4882a593Smuzhiyun                    os.path.exists(os.path.join(workdir, fname)) and
114*4882a593Smuzhiyun                    (srcabspath == workdir or not
115*4882a593Smuzhiyun                    os.path.join(workdir, fname).startswith(srcabspath +
116*4882a593Smuzhiyun                        os.sep))]
117*4882a593Smuzhiyun    if local_files:
118*4882a593Smuzhiyun        for fname in local_files:
119*4882a593Smuzhiyun            _move_file(os.path.join(workdir, fname),
120*4882a593Smuzhiyun                        os.path.join(tempdir, 'oe-local-files', fname))
121*4882a593Smuzhiyun        with open(os.path.join(tempdir, 'oe-local-files', '.gitignore'),
122*4882a593Smuzhiyun                    'w') as f:
123*4882a593Smuzhiyun            f.write('# Ignore local files, by default. Remove this file '
124*4882a593Smuzhiyun                    'if you want to commit the directory to Git\n*\n')
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun    if srcsubdir == workdir:
127*4882a593Smuzhiyun        # Find non-patch non-local sources that were "unpacked" to srctree
128*4882a593Smuzhiyun        # directory
129*4882a593Smuzhiyun        src_files = [fname for fname in _ls_tree(workdir) if
130*4882a593Smuzhiyun                        os.path.basename(fname) not in recipe_patches]
131*4882a593Smuzhiyun        srcsubdir = d.getVar('DEVTOOL_PATCH_SRCDIR')
132*4882a593Smuzhiyun        # Move source files to S
133*4882a593Smuzhiyun        for path in src_files:
134*4882a593Smuzhiyun            _move_file(os.path.join(workdir, path),
135*4882a593Smuzhiyun                        os.path.join(srcsubdir, path))
136*4882a593Smuzhiyun    elif os.path.dirname(srcsubdir) != workdir:
137*4882a593Smuzhiyun        # Handle if S is set to a subdirectory of the source
138*4882a593Smuzhiyun        srcsubdir = os.path.join(workdir, os.path.relpath(srcsubdir, workdir).split(os.sep)[0])
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun    scriptutils.git_convert_standalone_clone(srcsubdir)
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun    # Make sure that srcsubdir exists
143*4882a593Smuzhiyun    bb.utils.mkdirhier(srcsubdir)
144*4882a593Smuzhiyun    if not os.listdir(srcsubdir):
145*4882a593Smuzhiyun        bb.warn("No source unpacked to S - either the %s recipe "
146*4882a593Smuzhiyun                "doesn't use any source or the correct source "
147*4882a593Smuzhiyun                "directory could not be determined" % d.getVar('PN'))
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun    devbranch = d.getVar('DEVTOOL_DEVBRANCH')
150*4882a593Smuzhiyun    setup_git_repo(srcsubdir, d.getVar('PV'), devbranch, d=d)
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun    (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srcsubdir)
153*4882a593Smuzhiyun    initial_rev = stdout.rstrip()
154*4882a593Smuzhiyun    with open(os.path.join(tempdir, 'initial_rev'), 'w') as f:
155*4882a593Smuzhiyun        f.write(initial_rev)
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun    with open(os.path.join(tempdir, 'srcsubdir'), 'w') as f:
158*4882a593Smuzhiyun        f.write(srcsubdir)
159*4882a593Smuzhiyun}
160*4882a593Smuzhiyun
161*4882a593Smuzhiyunpython devtool_pre_patch() {
162*4882a593Smuzhiyun    if d.getVar('S') == d.getVar('WORKDIR'):
163*4882a593Smuzhiyun        d.setVar('S', '${DEVTOOL_PATCH_SRCDIR}')
164*4882a593Smuzhiyun}
165*4882a593Smuzhiyun
166*4882a593Smuzhiyunpython devtool_post_patch() {
167*4882a593Smuzhiyun    import shutil
168*4882a593Smuzhiyun    tempdir = d.getVar('DEVTOOL_TEMPDIR')
169*4882a593Smuzhiyun    with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f:
170*4882a593Smuzhiyun        srcsubdir = f.read()
171*4882a593Smuzhiyun    with open(os.path.join(tempdir, 'initial_rev'), 'r') as f:
172*4882a593Smuzhiyun        initial_rev = f.read()
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun    def rm_patches():
175*4882a593Smuzhiyun        patches_dir = os.path.join(srcsubdir, 'patches')
176*4882a593Smuzhiyun        if os.path.exists(patches_dir):
177*4882a593Smuzhiyun            shutil.rmtree(patches_dir)
178*4882a593Smuzhiyun        # Restore any "patches" directory that was actually part of the source tree
179*4882a593Smuzhiyun        try:
180*4882a593Smuzhiyun            bb.process.run('git checkout -- patches', cwd=srcsubdir)
181*4882a593Smuzhiyun        except bb.process.ExecutionError:
182*4882a593Smuzhiyun            pass
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun    extra_overrides = d.getVar('DEVTOOL_EXTRA_OVERRIDES')
185*4882a593Smuzhiyun    if extra_overrides:
186*4882a593Smuzhiyun        extra_overrides = set(extra_overrides.split(':'))
187*4882a593Smuzhiyun        devbranch = d.getVar('DEVTOOL_DEVBRANCH')
188*4882a593Smuzhiyun        default_overrides = d.getVar('OVERRIDES').split(':')
189*4882a593Smuzhiyun        no_overrides = []
190*4882a593Smuzhiyun        # First, we may have some overrides that are referred to in the recipe set in
191*4882a593Smuzhiyun        # our configuration, so we need to make a branch that excludes those
192*4882a593Smuzhiyun        for override in default_overrides:
193*4882a593Smuzhiyun            if override not in extra_overrides:
194*4882a593Smuzhiyun                no_overrides.append(override)
195*4882a593Smuzhiyun        if default_overrides != no_overrides:
196*4882a593Smuzhiyun            # Some overrides are active in the current configuration, so
197*4882a593Smuzhiyun            # we need to create a branch where none of the overrides are active
198*4882a593Smuzhiyun            bb.process.run('git checkout %s -b devtool-no-overrides' % initial_rev, cwd=srcsubdir)
199*4882a593Smuzhiyun            # Run do_patch function with the override applied
200*4882a593Smuzhiyun            localdata = bb.data.createCopy(d)
201*4882a593Smuzhiyun            localdata.setVar('OVERRIDES', ':'.join(no_overrides))
202*4882a593Smuzhiyun            localdata.setVar('FILESOVERRIDES', ':'.join(no_overrides))
203*4882a593Smuzhiyun            bb.build.exec_func('do_patch', localdata)
204*4882a593Smuzhiyun            rm_patches()
205*4882a593Smuzhiyun            # Now we need to reconcile the dev branch with the no-overrides one
206*4882a593Smuzhiyun            # (otherwise we'd likely be left with identical commits that have different hashes)
207*4882a593Smuzhiyun            bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir)
208*4882a593Smuzhiyun            bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir)
209*4882a593Smuzhiyun        else:
210*4882a593Smuzhiyun            bb.process.run('git checkout %s -b devtool-no-overrides' % devbranch, cwd=srcsubdir)
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun        for override in extra_overrides:
213*4882a593Smuzhiyun            localdata = bb.data.createCopy(d)
214*4882a593Smuzhiyun            if override in default_overrides:
215*4882a593Smuzhiyun                bb.process.run('git branch devtool-override-%s %s' % (override, devbranch), cwd=srcsubdir)
216*4882a593Smuzhiyun            else:
217*4882a593Smuzhiyun                # Reset back to the initial commit on a new branch
218*4882a593Smuzhiyun                bb.process.run('git checkout %s -b devtool-override-%s' % (initial_rev, override), cwd=srcsubdir)
219*4882a593Smuzhiyun                # Run do_patch function with the override applied
220*4882a593Smuzhiyun                localdata.setVar('OVERRIDES', ':'.join(no_overrides + [override]))
221*4882a593Smuzhiyun                localdata.setVar('FILESOVERRIDES', ':'.join(no_overrides + [override]))
222*4882a593Smuzhiyun                bb.build.exec_func('do_patch', localdata)
223*4882a593Smuzhiyun                rm_patches()
224*4882a593Smuzhiyun                # Now we need to reconcile the new branch with the no-overrides one
225*4882a593Smuzhiyun                # (otherwise we'd likely be left with identical commits that have different hashes)
226*4882a593Smuzhiyun                bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir)
227*4882a593Smuzhiyun        bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir)
228*4882a593Smuzhiyun    bb.process.run('git tag -f devtool-patched', cwd=srcsubdir)
229*4882a593Smuzhiyun}
230*4882a593Smuzhiyun
231*4882a593Smuzhiyunpython devtool_post_configure() {
232*4882a593Smuzhiyun    import shutil
233*4882a593Smuzhiyun    tempdir = d.getVar('DEVTOOL_TEMPDIR')
234*4882a593Smuzhiyun    shutil.copy2(os.path.join(d.getVar('B'), '.config'), tempdir)
235*4882a593Smuzhiyun}
236