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