| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 1 | # 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 |  | 
|  | 22 | DEVTOOL_TEMPDIR ?= "" | 
|  | 23 | DEVTOOL_PATCH_SRCDIR = "${DEVTOOL_TEMPDIR}/patchworkdir" | 
|  | 24 |  | 
|  | 25 |  | 
|  | 26 | python() { | 
|  | 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 |  | 
|  | 67 | python 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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 93 | is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 94 | # 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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 99 | if is_kernel_yocto: | 
| Brad Bishop | 64c979e | 2019-11-04 13:55:29 -0500 | [diff] [blame^] | 100 | for key in [f for f in local_files if f.endswith('scc')]: | 
|  | 101 | with open(local_files[key], 'r') as sccfile: | 
| Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 102 | for l in sccfile: | 
|  | 103 | line = l.split() | 
|  | 104 | if line and line[0] in ('kconf', 'patch'): | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 105 | cfg = os.path.join(os.path.dirname(local_files[key]), line[-1]) | 
| Brad Bishop | 64c979e | 2019-11-04 13:55:29 -0500 | [diff] [blame^] | 106 | if cfg not in local_files.values(): | 
| Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 107 | local_files[line[-1]] = cfg | 
|  | 108 | shutil.copy2(cfg, workdir) | 
| Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 109 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 110 | # Ignore local files with subdir={BP} | 
|  | 111 | srcabspath = os.path.abspath(srcsubdir) | 
|  | 112 | local_files = [fname for fname in local_files if | 
|  | 113 | os.path.exists(os.path.join(workdir, fname)) and | 
|  | 114 | (srcabspath == workdir or not | 
|  | 115 | os.path.join(workdir, fname).startswith(srcabspath + | 
|  | 116 | os.sep))] | 
|  | 117 | if local_files: | 
|  | 118 | for fname in local_files: | 
|  | 119 | _move_file(os.path.join(workdir, fname), | 
|  | 120 | os.path.join(tempdir, 'oe-local-files', fname)) | 
|  | 121 | with open(os.path.join(tempdir, 'oe-local-files', '.gitignore'), | 
|  | 122 | 'w') as f: | 
|  | 123 | f.write('# Ignore local files, by default. Remove this file ' | 
|  | 124 | 'if you want to commit the directory to Git\n*\n') | 
|  | 125 |  | 
|  | 126 | if srcsubdir == workdir: | 
|  | 127 | # Find non-patch non-local sources that were "unpacked" to srctree | 
|  | 128 | # directory | 
|  | 129 | src_files = [fname for fname in _ls_tree(workdir) if | 
|  | 130 | os.path.basename(fname) not in recipe_patches] | 
|  | 131 | srcsubdir = d.getVar('DEVTOOL_PATCH_SRCDIR') | 
|  | 132 | # Move source files to S | 
|  | 133 | for path in src_files: | 
|  | 134 | _move_file(os.path.join(workdir, path), | 
|  | 135 | os.path.join(srcsubdir, path)) | 
|  | 136 | elif os.path.dirname(srcsubdir) != workdir: | 
|  | 137 | # Handle if S is set to a subdirectory of the source | 
|  | 138 | srcsubdir = os.path.join(workdir, os.path.relpath(srcsubdir, workdir).split(os.sep)[0]) | 
|  | 139 |  | 
|  | 140 | scriptutils.git_convert_standalone_clone(srcsubdir) | 
|  | 141 |  | 
|  | 142 | # Make sure that srcsubdir exists | 
|  | 143 | bb.utils.mkdirhier(srcsubdir) | 
|  | 144 | if not os.listdir(srcsubdir): | 
|  | 145 | bb.warn("No source unpacked to S - either the %s recipe " | 
|  | 146 | "doesn't use any source or the correct source " | 
|  | 147 | "directory could not be determined" % d.getVar('PN')) | 
|  | 148 |  | 
|  | 149 | devbranch = d.getVar('DEVTOOL_DEVBRANCH') | 
|  | 150 | setup_git_repo(srcsubdir, d.getVar('PV'), devbranch, d=d) | 
|  | 151 |  | 
|  | 152 | (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srcsubdir) | 
|  | 153 | initial_rev = stdout.rstrip() | 
|  | 154 | with open(os.path.join(tempdir, 'initial_rev'), 'w') as f: | 
|  | 155 | f.write(initial_rev) | 
|  | 156 |  | 
|  | 157 | with open(os.path.join(tempdir, 'srcsubdir'), 'w') as f: | 
|  | 158 | f.write(srcsubdir) | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | python devtool_pre_patch() { | 
|  | 162 | if d.getVar('S') == d.getVar('WORKDIR'): | 
|  | 163 | d.setVar('S', '${DEVTOOL_PATCH_SRCDIR}') | 
|  | 164 | } | 
|  | 165 |  | 
|  | 166 | python devtool_post_patch() { | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 167 | import shutil | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 168 | tempdir = d.getVar('DEVTOOL_TEMPDIR') | 
|  | 169 | with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f: | 
|  | 170 | srcsubdir = f.read() | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 171 | with open(os.path.join(tempdir, 'initial_rev'), 'r') as f: | 
|  | 172 | initial_rev = f.read() | 
|  | 173 |  | 
|  | 174 | def rm_patches(): | 
|  | 175 | patches_dir = os.path.join(srcsubdir, 'patches') | 
|  | 176 | if os.path.exists(patches_dir): | 
|  | 177 | shutil.rmtree(patches_dir) | 
|  | 178 | # Restore any "patches" directory that was actually part of the source tree | 
|  | 179 | try: | 
|  | 180 | bb.process.run('git checkout -- patches', cwd=srcsubdir) | 
|  | 181 | except bb.process.ExecutionError: | 
|  | 182 | pass | 
|  | 183 |  | 
|  | 184 | extra_overrides = d.getVar('DEVTOOL_EXTRA_OVERRIDES') | 
|  | 185 | if extra_overrides: | 
| Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 186 | extra_overrides = set(extra_overrides.split(':')) | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 187 | devbranch = d.getVar('DEVTOOL_DEVBRANCH') | 
|  | 188 | default_overrides = d.getVar('OVERRIDES').split(':') | 
|  | 189 | no_overrides = [] | 
|  | 190 | # First, we may have some overrides that are referred to in the recipe set in | 
|  | 191 | # our configuration, so we need to make a branch that excludes those | 
|  | 192 | for override in default_overrides: | 
| Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 193 | if override not in extra_overrides: | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 194 | no_overrides.append(override) | 
|  | 195 | if default_overrides != no_overrides: | 
|  | 196 | # Some overrides are active in the current configuration, so | 
|  | 197 | # we need to create a branch where none of the overrides are active | 
|  | 198 | bb.process.run('git checkout %s -b devtool-no-overrides' % initial_rev, cwd=srcsubdir) | 
|  | 199 | # Run do_patch function with the override applied | 
|  | 200 | localdata = bb.data.createCopy(d) | 
|  | 201 | localdata.setVar('OVERRIDES', ':'.join(no_overrides)) | 
|  | 202 | bb.build.exec_func('do_patch', localdata) | 
|  | 203 | rm_patches() | 
|  | 204 | # Now we need to reconcile the dev branch with the no-overrides one | 
|  | 205 | # (otherwise we'd likely be left with identical commits that have different hashes) | 
|  | 206 | bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir) | 
|  | 207 | bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir) | 
|  | 208 | else: | 
|  | 209 | bb.process.run('git checkout %s -b devtool-no-overrides' % devbranch, cwd=srcsubdir) | 
|  | 210 |  | 
| Brad Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 211 | for override in extra_overrides: | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 212 | localdata = bb.data.createCopy(d) | 
|  | 213 | if override in default_overrides: | 
|  | 214 | bb.process.run('git branch devtool-override-%s %s' % (override, devbranch), cwd=srcsubdir) | 
|  | 215 | else: | 
|  | 216 | # Reset back to the initial commit on a new branch | 
|  | 217 | bb.process.run('git checkout %s -b devtool-override-%s' % (initial_rev, override), cwd=srcsubdir) | 
|  | 218 | # Run do_patch function with the override applied | 
|  | 219 | localdata.appendVar('OVERRIDES', ':%s' % override) | 
|  | 220 | bb.build.exec_func('do_patch', localdata) | 
|  | 221 | rm_patches() | 
|  | 222 | # Now we need to reconcile the new branch with the no-overrides one | 
|  | 223 | # (otherwise we'd likely be left with identical commits that have different hashes) | 
|  | 224 | bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir) | 
|  | 225 | bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir) | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 226 | bb.process.run('git tag -f devtool-patched', cwd=srcsubdir) | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | python devtool_post_configure() { | 
|  | 230 | import shutil | 
|  | 231 | tempdir = d.getVar('DEVTOOL_TEMPDIR') | 
|  | 232 | shutil.copy2(os.path.join(d.getVar('B'), '.config'), tempdir) | 
|  | 233 | } |