devtool-source.bbclass 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. DEVTOOL_TEMPDIR ?= ""
  21. DEVTOOL_PATCH_SRCDIR = "${DEVTOOL_TEMPDIR}/patchworkdir"
  22. python() {
  23. tempdir = d.getVar('DEVTOOL_TEMPDIR')
  24. if not tempdir:
  25. bb.fatal('devtool-source class is for internal use by devtool only')
  26. # Make a subdir so we guard against WORKDIR==S
  27. workdir = os.path.join(tempdir, 'workdir')
  28. d.setVar('WORKDIR', workdir)
  29. if not d.getVar('S').startswith(workdir):
  30. # Usually a shared workdir recipe (kernel, gcc)
  31. # Try to set a reasonable default
  32. if bb.data.inherits_class('kernel', d):
  33. d.setVar('S', '${WORKDIR}/source')
  34. else:
  35. d.setVar('S', '${WORKDIR}/%s' % os.path.basename(d.getVar('S')))
  36. if bb.data.inherits_class('kernel', d):
  37. # We don't want to move the source to STAGING_KERNEL_DIR here
  38. d.setVar('STAGING_KERNEL_DIR', '${S}')
  39. d.setVar('STAMPS_DIR', os.path.join(tempdir, 'stamps'))
  40. d.setVar('T', os.path.join(tempdir, 'temp'))
  41. # Hook in pre/postfuncs
  42. is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
  43. if is_kernel_yocto:
  44. unpacktask = 'do_kernel_checkout'
  45. d.appendVarFlag('do_configure', 'postfuncs', ' devtool_post_configure')
  46. else:
  47. unpacktask = 'do_unpack'
  48. d.appendVarFlag(unpacktask, 'postfuncs', ' devtool_post_unpack')
  49. d.prependVarFlag('do_patch', 'prefuncs', ' devtool_pre_patch')
  50. d.appendVarFlag('do_patch', 'postfuncs', ' devtool_post_patch')
  51. # NOTE: in order for the patch stuff to be fully functional,
  52. # PATCHTOOL and PATCH_COMMIT_FUNCTIONS need to be set; we can't
  53. # do that here because we can't guarantee the order of the anonymous
  54. # functions, so it gets done in the bbappend we create.
  55. }
  56. python devtool_post_unpack() {
  57. import oe.recipeutils
  58. import shutil
  59. sys.path.insert(0, os.path.join(d.getVar('COREBASE'), 'scripts', 'lib'))
  60. import scriptutils
  61. from devtool import setup_git_repo
  62. tempdir = d.getVar('DEVTOOL_TEMPDIR')
  63. workdir = d.getVar('WORKDIR')
  64. srcsubdir = d.getVar('S')
  65. def _move_file(src, dst):
  66. """Move a file. Creates all the directory components of destination path."""
  67. dst_d = os.path.dirname(dst)
  68. if dst_d:
  69. bb.utils.mkdirhier(dst_d)
  70. shutil.move(src, dst)
  71. def _ls_tree(directory):
  72. """Recursive listing of files in a directory"""
  73. ret = []
  74. for root, dirs, files in os.walk(directory):
  75. ret.extend([os.path.relpath(os.path.join(root, fname), directory) for
  76. fname in files])
  77. return ret
  78. is_kernel_yocto = bb.data.inherits_class('kernel-yocto', d)
  79. # Move local source files into separate subdir
  80. recipe_patches = [os.path.basename(patch) for patch in
  81. oe.recipeutils.get_recipe_patches(d)]
  82. local_files = oe.recipeutils.get_recipe_local_files(d)
  83. if is_kernel_yocto:
  84. for key in [f for f in local_files if f.endswith('scc')]:
  85. with open(local_files[key], 'r') as sccfile:
  86. for l in sccfile:
  87. line = l.split()
  88. if line and line[0] in ('kconf', 'patch'):
  89. cfg = os.path.join(os.path.dirname(local_files[key]), line[-1])
  90. if cfg not in local_files.values():
  91. local_files[line[-1]] = cfg
  92. shutil.copy2(cfg, workdir)
  93. # Ignore local files with subdir={BP}
  94. srcabspath = os.path.abspath(srcsubdir)
  95. local_files = [fname for fname in local_files if
  96. os.path.exists(os.path.join(workdir, fname)) and
  97. (srcabspath == workdir or not
  98. os.path.join(workdir, fname).startswith(srcabspath +
  99. os.sep))]
  100. if local_files:
  101. for fname in local_files:
  102. _move_file(os.path.join(workdir, fname),
  103. os.path.join(tempdir, 'oe-local-files', fname))
  104. with open(os.path.join(tempdir, 'oe-local-files', '.gitignore'),
  105. 'w') as f:
  106. f.write('# Ignore local files, by default. Remove this file '
  107. 'if you want to commit the directory to Git\n*\n')
  108. if srcsubdir == workdir:
  109. # Find non-patch non-local sources that were "unpacked" to srctree
  110. # directory
  111. src_files = [fname for fname in _ls_tree(workdir) if
  112. os.path.basename(fname) not in recipe_patches]
  113. srcsubdir = d.getVar('DEVTOOL_PATCH_SRCDIR')
  114. # Move source files to S
  115. for path in src_files:
  116. _move_file(os.path.join(workdir, path),
  117. os.path.join(srcsubdir, path))
  118. elif os.path.dirname(srcsubdir) != workdir:
  119. # Handle if S is set to a subdirectory of the source
  120. srcsubdir = os.path.join(workdir, os.path.relpath(srcsubdir, workdir).split(os.sep)[0])
  121. scriptutils.git_convert_standalone_clone(srcsubdir)
  122. # Make sure that srcsubdir exists
  123. bb.utils.mkdirhier(srcsubdir)
  124. if not os.listdir(srcsubdir):
  125. bb.warn("No source unpacked to S - either the %s recipe "
  126. "doesn't use any source or the correct source "
  127. "directory could not be determined" % d.getVar('PN'))
  128. devbranch = d.getVar('DEVTOOL_DEVBRANCH')
  129. setup_git_repo(srcsubdir, d.getVar('PV'), devbranch, d=d)
  130. (stdout, _) = bb.process.run('git rev-parse HEAD', cwd=srcsubdir)
  131. initial_rev = stdout.rstrip()
  132. with open(os.path.join(tempdir, 'initial_rev'), 'w') as f:
  133. f.write(initial_rev)
  134. with open(os.path.join(tempdir, 'srcsubdir'), 'w') as f:
  135. f.write(srcsubdir)
  136. }
  137. python devtool_pre_patch() {
  138. if d.getVar('S') == d.getVar('WORKDIR'):
  139. d.setVar('S', '${DEVTOOL_PATCH_SRCDIR}')
  140. }
  141. python devtool_post_patch() {
  142. import shutil
  143. tempdir = d.getVar('DEVTOOL_TEMPDIR')
  144. with open(os.path.join(tempdir, 'srcsubdir'), 'r') as f:
  145. srcsubdir = f.read()
  146. with open(os.path.join(tempdir, 'initial_rev'), 'r') as f:
  147. initial_rev = f.read()
  148. def rm_patches():
  149. patches_dir = os.path.join(srcsubdir, 'patches')
  150. if os.path.exists(patches_dir):
  151. shutil.rmtree(patches_dir)
  152. # Restore any "patches" directory that was actually part of the source tree
  153. try:
  154. bb.process.run('git checkout -- patches', cwd=srcsubdir)
  155. except bb.process.ExecutionError:
  156. pass
  157. extra_overrides = d.getVar('DEVTOOL_EXTRA_OVERRIDES')
  158. if extra_overrides:
  159. extra_overrides = set(extra_overrides.split(':'))
  160. devbranch = d.getVar('DEVTOOL_DEVBRANCH')
  161. default_overrides = d.getVar('OVERRIDES').split(':')
  162. no_overrides = []
  163. # First, we may have some overrides that are referred to in the recipe set in
  164. # our configuration, so we need to make a branch that excludes those
  165. for override in default_overrides:
  166. if override not in extra_overrides:
  167. no_overrides.append(override)
  168. if default_overrides != no_overrides:
  169. # Some overrides are active in the current configuration, so
  170. # we need to create a branch where none of the overrides are active
  171. bb.process.run('git checkout %s -b devtool-no-overrides' % initial_rev, cwd=srcsubdir)
  172. # Run do_patch function with the override applied
  173. localdata = bb.data.createCopy(d)
  174. localdata.setVar('OVERRIDES', ':'.join(no_overrides))
  175. localdata.setVar('FILESOVERRIDES', ':'.join(no_overrides))
  176. bb.build.exec_func('do_patch', localdata)
  177. rm_patches()
  178. # Now we need to reconcile the dev branch with the no-overrides one
  179. # (otherwise we'd likely be left with identical commits that have different hashes)
  180. bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir)
  181. bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir)
  182. else:
  183. bb.process.run('git checkout %s -b devtool-no-overrides' % devbranch, cwd=srcsubdir)
  184. for override in extra_overrides:
  185. localdata = bb.data.createCopy(d)
  186. if override in default_overrides:
  187. bb.process.run('git branch devtool-override-%s %s' % (override, devbranch), cwd=srcsubdir)
  188. else:
  189. # Reset back to the initial commit on a new branch
  190. bb.process.run('git checkout %s -b devtool-override-%s' % (initial_rev, override), cwd=srcsubdir)
  191. # Run do_patch function with the override applied
  192. localdata.setVar('OVERRIDES', ':'.join(no_overrides + [override]))
  193. localdata.setVar('FILESOVERRIDES', ':'.join(no_overrides + [override]))
  194. bb.build.exec_func('do_patch', localdata)
  195. rm_patches()
  196. # Now we need to reconcile the new branch with the no-overrides one
  197. # (otherwise we'd likely be left with identical commits that have different hashes)
  198. bb.process.run('git rebase devtool-no-overrides', cwd=srcsubdir)
  199. bb.process.run('git checkout %s' % devbranch, cwd=srcsubdir)
  200. bb.process.run('git tag -f devtool-patched', cwd=srcsubdir)
  201. }
  202. python devtool_post_configure() {
  203. import shutil
  204. tempdir = d.getVar('DEVTOOL_TEMPDIR')
  205. shutil.copy2(os.path.join(d.getVar('B'), '.config'), tempdir)
  206. }