| 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: | 
 | 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 Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 106 |                         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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 110 |                 sccfile.close() | 
 | 111 |  | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 112 |     # 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 |  | 
 | 163 | python devtool_pre_patch() { | 
 | 164 |     if d.getVar('S') == d.getVar('WORKDIR'): | 
 | 165 |         d.setVar('S', '${DEVTOOL_PATCH_SRCDIR}') | 
 | 166 | } | 
 | 167 |  | 
 | 168 | python devtool_post_patch() { | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 169 |     import shutil | 
| Brad Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 170 |     tempdir = d.getVar('DEVTOOL_TEMPDIR') | 
 | 171 |     with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f: | 
 | 172 |         srcsubdir = f.read() | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 173 |     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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 188 |         extra_overrides = set(extra_overrides.split(':')) | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 189 |         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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 195 |             if override not in extra_overrides: | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 196 |                 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 Bishop | 1a4b7ee | 2018-12-16 17:11:34 -0800 | [diff] [blame] | 213 |         for override in extra_overrides: | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 214 |             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 Bishop | d7bf8c1 | 2018-02-25 22:55:05 -0500 | [diff] [blame] | 228 |     bb.process.run('git tag -f devtool-patched', cwd=srcsubdir) | 
 | 229 | } | 
 | 230 |  | 
 | 231 | python devtool_post_configure() { | 
 | 232 |     import shutil | 
 | 233 |     tempdir = d.getVar('DEVTOOL_TEMPDIR') | 
 | 234 |     shutil.copy2(os.path.join(d.getVar('B'), '.config'), tempdir) | 
 | 235 | } |