external-toolchain.bbclass 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. # This class provides everything necessary for a recipe to pull bits from an
  2. # external toolchain:
  3. # - Automatically sets LIC_FILES_CHKSUM based on LICENSE if appropriate
  4. # - Searches the external toolchain sysroot and alternate locations for the
  5. # patterns specified in the FILES variables, with support for checking
  6. # alternate locations within the sysroot as well
  7. # - Automatically PROVIDES/RPROVIDES the non-external-suffixed names
  8. # - Usual bits to handle packaging of existing binaries
  9. # - Automatically skips the recipe if its files aren't available in the
  10. # external toolchain
  11. # - Automatically grabs all the .debug files for everything included
  12. # Since these are prebuilt binaries, there are no source files to checksum for
  13. # LIC_FILES_CHKSUM, so use the license from common-licenses
  14. inherit common-license
  15. # We don't extract anything which will create S, and we don't want to see the
  16. # warning about it
  17. S = "${WORKDIR}"
  18. # Prebuilt binaries, no need for any default dependencies
  19. INHIBIT_DEFAULT_DEPS = "1"
  20. # Missing build deps don't matter when we don't build anything
  21. INSANE_SKIP_${PN} += "build-deps"
  22. EXTERNAL_PN ?= "${@PN.replace('-external', '')}"
  23. PROVIDES += "${EXTERNAL_PN}"
  24. LICENSE = "CLOSED"
  25. LIC_FILES_CHKSUM = "${COMMON_LIC_CHKSUM}"
  26. EXTERNAL_TOOLCHAIN_SYSROOT ?= "${@external_run(d, 'gcc', *(TARGET_CC_ARCH.split() + ['-print-sysroot'])).rstrip()}"
  27. EXTERNAL_TOOLCHAIN_LIBROOT ?= "${@external_run(d, 'gcc', *(TARGET_CC_ARCH.split() + ['-print-file-name=crtbegin.o'])).rstrip().replace('/crtbegin.o', '')}"
  28. EXTERNAL_INSTALL_SOURCE_PATHS = "\
  29. ${EXTERNAL_TOOLCHAIN_SYSROOT} \
  30. ${EXTERNAL_TOOLCHAIN}/${EXTERNAL_TARGET_SYS} \
  31. ${EXTERNAL_TOOLCHAIN_SYSROOT}/.. \
  32. ${EXTERNAL_TOOLCHAIN} \
  33. ${D} \
  34. "
  35. # Potential locations within the external toolchain sysroot
  36. FILES_MIRRORS = "\
  37. ${bindir}/|/usr/${baselib}/bin/\n \
  38. ${base_libdir}/|/usr/${baselib}/\n \
  39. ${libexecdir}/|/usr/libexec/\n \
  40. ${libexecdir}/|/usr/${baselib}/${PN}\n \
  41. ${mandir}/|/usr/share/man/\n \
  42. ${mandir}/|/usr/man/\n \
  43. ${mandir}/|/man/\n \
  44. ${mandir}/|/share/doc/*-${EXTERNAL_TARGET_SYS}/man/\n \
  45. ${prefix}/|${base_prefix}/\n \
  46. "
  47. do_configure[noexec] = "1"
  48. do_compile[noexec] = "1"
  49. EXTERNAL_PV_PREFIX ?= ""
  50. EXTERNAL_PV_SUFFIX ?= ""
  51. PV_prepend = "${@'${EXTERNAL_PV_PREFIX}' if '${EXTERNAL_PV_PREFIX}' else ''}"
  52. PV_append = "${@'${EXTERNAL_PV_SUFFIX}' if '${EXTERNAL_PV_SUFFIX}' else ''}"
  53. EXTERNAL_EXTRA_FILES ?= ""
  54. # Skip this recipe if we don't have files in the external toolchain
  55. EXTERNAL_AUTO_PROVIDE ?= "0"
  56. EXTERNAL_AUTO_PROVIDE[type] = "boolean"
  57. EXTERNAL_AUTO_PROVIDE_class-target ?= "1"
  58. # We don't care if this path references other variables
  59. EXTERNAL_TOOLCHAIN[vardepvalue] = "${EXTERNAL_TOOLCHAIN}"
  60. # We don't want to rebuild if the path to the toolchain changes, only if the
  61. # toolchain changes
  62. external_toolchain_do_install[vardepsexclude] += "EXTERNAL_TOOLCHAIN"
  63. EXTERNAL_INSTALL_SOURCE_PATHS[vardepsexclude] += "EXTERNAL_TOOLCHAIN"
  64. python () {
  65. # Skipping only matters up front
  66. if d.getVar('BB_WORKERCONTEXT', True) == '1':
  67. return
  68. # We're not an available provider if there's no external toolchain
  69. if not d.getVar("EXTERNAL_TOOLCHAIN"):
  70. raise bb.parse.SkipPackage("External toolchain not configured (EXTERNAL_TOOLCHAIN not set).")
  71. if not oe.data.typed_value('EXTERNAL_AUTO_PROVIDE', d):
  72. return
  73. sysroots, mirrors = get_file_search_metadata(d)
  74. pattern = d.getVar('EXTERNAL_PROVIDE_PATTERN', True)
  75. if pattern is None:
  76. files = list(gather_pkg_files(d))
  77. files = filter(lambda f: '.debug' not in f, files)
  78. expanded = expand_paths(files, mirrors)
  79. paths = search_sysroots(expanded, sysroots)
  80. if not any(f for p, f in paths):
  81. raise bb.parse.SkipPackage('No files found in external toolchain sysroot for `{}`'.format(', '.join(files)))
  82. elif not pattern:
  83. return
  84. else:
  85. expanded = oe.external_toolchain.expand_paths([pattern], mirrors)
  86. paths = oe.external_toolchain.search_sysroots(expanded, sysroots)
  87. if not any(f for p, f in paths):
  88. raise bb.parse.SkipPackage('No files found in external toolchain sysroot for `{}`'.format(pattern))
  89. }
  90. python do_install () {
  91. bb.build.exec_func('external_toolchain_do_install', d)
  92. if 'do_install_extra' in d:
  93. bb.build.exec_func('do_install_extra', d)
  94. }
  95. python external_toolchain_do_install () {
  96. import subprocess
  97. installdest = d.getVar('D', True)
  98. sysroots, mirrors = get_file_search_metadata(d)
  99. files = gather_pkg_files(d)
  100. copy_from_sysroots(files, sysroots, mirrors, installdest)
  101. subprocess.check_call(['chown', '-R', 'root:root', installdest])
  102. }
  103. external_toolchain_do_install[vardeps] += "${@' '.join('FILES_%s' % pkg for pkg in '${PACKAGES}'.split())}"
  104. def get_file_search_metadata(d):
  105. '''Given the metadata, return the mirrors and sysroots to operate against.'''
  106. from collections import defaultdict
  107. mirrors = []
  108. for entry in d.getVar('FILES_MIRRORS', True).replace('\\n', '\n').split('\n'):
  109. entry = entry.strip()
  110. if not entry:
  111. continue
  112. pattern, subst = entry.strip().split('|', 1)
  113. mirrors.append(('^' + pattern, subst))
  114. source_paths = [os.path.realpath(p)
  115. for p in d.getVar('EXTERNAL_INSTALL_SOURCE_PATHS', True).split()]
  116. return source_paths, mirrors
  117. def gather_pkg_files(d):
  118. '''Given the metadata, return all the files we want to copy to ${D} for
  119. this recipe.'''
  120. import itertools
  121. files = []
  122. for pkg in d.getVar('PACKAGES', True).split():
  123. files = itertools.chain(files, (d.getVar('FILES_{}'.format(pkg), True) or '').split())
  124. files = itertools.chain(files, d.getVar('EXTERNAL_EXTRA_FILES', True).split())
  125. return files
  126. def copy_from_sysroots(pathnames, sysroots, mirrors, installdest):
  127. '''Copy the specified files from the specified sysroots, also checking the
  128. specified mirror patterns as alternate paths, to the specified destination.'''
  129. import subprocess
  130. expanded_pathnames = expand_paths(pathnames, mirrors)
  131. searched_paths = search_sysroots(expanded_pathnames, sysroots)
  132. for path, files in searched_paths:
  133. if not files:
  134. bb.debug(1, 'Failed to find `{}`'.format(path))
  135. else:
  136. destdir = oe.path.join(installdest, os.path.dirname(path))
  137. bb.utils.mkdirhier(destdir)
  138. subprocess.check_call(['cp', '-pPR'] + list(files) + [destdir + '/'])
  139. bb.note('Copied `{}` to `{}/`'.format(', '.join(files), destdir))
  140. def expand_paths(pathnames, mirrors):
  141. '''Apply search/replace to paths to get alternate search paths.
  142. Returns a generator with tuples of (pathname, expanded_paths).'''
  143. import re
  144. for pathname in pathnames:
  145. expanded_paths = [pathname]
  146. for search, replace in mirrors:
  147. try:
  148. new_pathname = re.sub(search, replace, pathname, count=1)
  149. except re.error as exc:
  150. bb.warn("Invalid pattern for `%s`" % search)
  151. continue
  152. if new_pathname != pathname:
  153. expanded_paths.append(new_pathname)
  154. yield pathname, expanded_paths
  155. def search_sysroots(path_entries, sysroots):
  156. '''Search the supplied sysroots for the supplied paths, checking supplied
  157. alternate paths. Expects entries in the format (pathname, all_paths).
  158. Returns a generator with tuples of (pathname, found_paths).'''
  159. import glob
  160. import itertools
  161. for path, pathnames in path_entries:
  162. for sysroot, pathname in ((s, p) for s in sysroots
  163. for p in itertools.chain([path], pathnames)):
  164. check_path = sysroot + os.sep + pathname
  165. found_paths = glob.glob(check_path)
  166. if found_paths:
  167. yield path, found_paths
  168. break
  169. else:
  170. yield path, None
  171. def find_sysroot_files(paths, d):
  172. sysroots, mirrors = get_file_search_metadata(d)
  173. expanded = expand_paths(paths, mirrors)
  174. search_results = search_sysroots(expanded, sysroots)
  175. return [v for k, v in search_results]
  176. # Change do_install's CWD to EXTERNAL_TOOLCHAIN for convenience
  177. do_install[dirs] = "${D} ${EXTERNAL_TOOLCHAIN}"
  178. # Debug files are likely already split out
  179. INHIBIT_PACKAGE_STRIP = "1"
  180. # Toolchain shipped binaries weren't necessarily built ideally
  181. WARN_QA_remove = "ldflags textrel"
  182. ERROR_QA_remove = "ldflags textrel"
  183. RPROVIDES_${PN} += "${EXTERNAL_PN}"
  184. RPROVIDES_${PN}-dev += "${EXTERNAL_PN}-dev"
  185. RPROVIDES_${PN}-staticdev += "${EXTERNAL_PN}-staticdev"
  186. RPROVIDES_${PN}-dbg += "${EXTERNAL_PN}-dbg"
  187. RPROVIDES_${PN}-doc += "${EXTERNAL_PN}-doc"
  188. RPROVIDES_${PN}-locale += "${EXTERNAL_PN}-locale"
  189. LOCALEBASEPN = "${EXTERNAL_PN}"
  190. FILES_${PN} = ""
  191. FILES_${PN}-dev = ""
  192. FILES_${PN}-staticdev = ""
  193. FILES_${PN}-doc = ""
  194. FILES_${PN}-locale = ""
  195. def debug_paths(d):
  196. l = d.createCopy()
  197. l.finalize()
  198. paths = []
  199. exclude = [
  200. l.getVar('datadir', True),
  201. l.getVar('includedir', True),
  202. ]
  203. for p in l.getVar('PACKAGES', True).split():
  204. if p.endswith('-dbg'):
  205. continue
  206. for f in (l.getVar('FILES_%s' % p, True) or '').split():
  207. if any((f == x or f.startswith(x + '/')) for x in exclude):
  208. continue
  209. d = os.path.dirname(f)
  210. b = os.path.basename(f)
  211. paths.append('/usr/lib/debug{0}/{1}.debug'.format(d, b))
  212. paths.append('{0}/.debug/{1}'.format(d, b))
  213. paths.append('{0}/.debug/{1}.debug'.format(d, b))
  214. return set(paths)
  215. FILES_${PN}-dbg = "${@' '.join(debug_paths(d))}"
  216. # do_package[depends] += "virtual/${MLPREFIX}libc:do_packagedata"
  217. # do_package_write_ipk[depends] += "virtual/${MLPREFIX}libc:do_packagedata"
  218. # do_package_write_deb[depends] += "virtual/${MLPREFIX}libc:do_packagedata"
  219. # do_package_write_rpm[depends] += "virtual/${MLPREFIX}libc:do_packagedata"