icecc.bbclass 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. # IceCream distributed compiling support
  2. #
  3. # Stages directories with symlinks from gcc/g++ to icecc, for both
  4. # native and cross compilers. Depending on each configure or compile,
  5. # the directories are added at the head of the PATH list and ICECC_CXX
  6. # and ICEC_CC are set.
  7. #
  8. # For the cross compiler, creates a tar.gz of our toolchain and sets
  9. # ICECC_VERSION accordingly.
  10. #
  11. # The class now handles all 3 different compile 'stages' (i.e native ,cross-kernel and target) creating the
  12. # necessary environment tar.gz file to be used by the remote machines.
  13. # It also supports meta-toolchain generation
  14. #
  15. # If ICECC_PATH is not set in local.conf then the class will try to locate it using 'bb.utils.which'
  16. # but nothing is sure ;)
  17. #
  18. # If ICECC_ENV_EXEC is set in local.conf, then it should point to the icecc-create-env script provided by the user
  19. # or the default one provided by icecc-create-env.bb will be used
  20. # (NOTE that this is a modified version of the script need it and *not the one that comes with icecc*
  21. #
  22. # User can specify if specific packages or packages belonging to class should not use icecc to distribute
  23. # compile jobs to remote machines, but handled locally, by defining ICECC_USER_CLASS_BL and ICECC_USER_PACKAGE_BL
  24. # with the appropriate values in local.conf. In addition the user can force to enable icecc for packages
  25. # which set an empty PARALLEL_MAKE variable by defining ICECC_USER_PACKAGE_WL.
  26. #
  27. #########################################################################################
  28. #Error checking is kept to minimum so double check any parameters you pass to the class
  29. ###########################################################################################
  30. BB_HASHBASE_WHITELIST += "ICECC_PARALLEL_MAKE ICECC_DISABLED ICECC_USER_PACKAGE_BL \
  31. ICECC_USER_CLASS_BL ICECC_USER_PACKAGE_WL ICECC_PATH ICECC_ENV_EXEC \
  32. ICECC_CARET_WORKAROUND ICECC_CFLAGS ICECC_ENV_VERSION \
  33. ICECC_DEBUG ICECC_LOGFILE ICECC_REPEAT_RATE ICECC_PREFERRED_HOST \
  34. ICECC_CLANG_REMOTE_CPP ICECC_IGNORE_UNVERIFIED ICECC_TEST_SOCKET \
  35. ICECC_ENV_DEBUG ICECC_SYSTEM_PACKAGE_BL ICECC_SYSTEM_CLASS_BL \
  36. ICECC_REMOTE_CPP \
  37. "
  38. ICECC_ENV_EXEC ?= "${STAGING_BINDIR_NATIVE}/icecc-create-env"
  39. HOSTTOOLS_NONFATAL += "icecc patchelf"
  40. # This version can be incremented when changes are made to the environment that
  41. # invalidate the version on the compile nodes. Changing it will cause a new
  42. # environment to be created.
  43. #
  44. # A useful thing to do for testing Icecream changes locally is to add a
  45. # subversion in local.conf:
  46. # ICECC_ENV_VERSION_append = "-my-ver-1"
  47. ICECC_ENV_VERSION = "2"
  48. # Default to disabling the caret workaround, If set to "1" in local.conf, icecc
  49. # will locally recompile any files that have warnings, which can adversely
  50. # affect performance.
  51. #
  52. # See: https://github.com/icecc/icecream/issues/190
  53. export ICECC_CARET_WORKAROUND ??= "0"
  54. export ICECC_REMOTE_CPP ??= "0"
  55. ICECC_CFLAGS = ""
  56. CFLAGS += "${ICECC_CFLAGS}"
  57. CXXFLAGS += "${ICECC_CFLAGS}"
  58. # Debug flags when generating environments
  59. ICECC_ENV_DEBUG ??= ""
  60. # "system" recipe blacklist contains a list of packages that can not distribute
  61. # compile tasks for one reason or the other. When adding new entry, please
  62. # document why (how it failed) so that we can re-evaluate it later e.g. when
  63. # there is new version
  64. #
  65. # libgcc-initial - fails with CPP sanity check error if host sysroot contains
  66. # cross gcc built for another target tune/variant
  67. # pixman - prng_state: TLS reference mismatches non-TLS reference, possibly due to
  68. # pragma omp threadprivate(prng_state)
  69. # systemtap - _HelperSDT.c undefs macros and uses the identifiers in macros emitting
  70. # inline assembly
  71. # target-sdk-provides-dummy - ${HOST_PREFIX} is empty which triggers the "NULL
  72. # prefix" error.
  73. ICECC_SYSTEM_PACKAGE_BL += "\
  74. libgcc-initial \
  75. pixman \
  76. systemtap \
  77. target-sdk-provides-dummy \
  78. "
  79. # "system" classes that should be blacklisted. When adding new entry, please
  80. # document why (how it failed) so that we can re-evaluate it later
  81. #
  82. # image - Image aren't compiling, but the testing framework for images captures
  83. # PARALLEL_MAKE as part of the test environment. Many tests won't use
  84. # icecream, but leaving the high level of parallelism can cause them to
  85. # consume an unnecessary amount of resources.
  86. ICECC_SYSTEM_CLASS_BL += "\
  87. image \
  88. "
  89. def icecc_dep_prepend(d):
  90. # INHIBIT_DEFAULT_DEPS doesn't apply to the patch command. Whether or not
  91. # we need that built is the responsibility of the patch function / class, not
  92. # the application.
  93. if not d.getVar('INHIBIT_DEFAULT_DEPS'):
  94. return "icecc-create-env-native"
  95. return ""
  96. DEPENDS_prepend = "${@icecc_dep_prepend(d)} "
  97. get_cross_kernel_cc[vardepsexclude] += "KERNEL_CC"
  98. def get_cross_kernel_cc(bb,d):
  99. if not icecc_is_kernel(bb, d):
  100. return None
  101. # evaluate the expression by the shell if necessary
  102. kernel_cc = d.getVar('KERNEL_CC')
  103. if '`' in kernel_cc or '$(' in kernel_cc:
  104. import subprocess
  105. kernel_cc = subprocess.check_output("echo %s" % kernel_cc, shell=True).decode("utf-8")[:-1]
  106. kernel_cc = kernel_cc.replace('ccache', '').strip()
  107. kernel_cc = kernel_cc.split(' ')[0]
  108. kernel_cc = kernel_cc.strip()
  109. return kernel_cc
  110. def get_icecc(d):
  111. return d.getVar('ICECC_PATH') or bb.utils.which(os.getenv("PATH"), "icecc")
  112. def use_icecc(bb,d):
  113. if d.getVar('ICECC_DISABLED') == "1":
  114. # don't even try it, when explicitly disabled
  115. return "no"
  116. # allarch recipes don't use compiler
  117. if icecc_is_allarch(bb, d):
  118. return "no"
  119. if icecc_is_cross_canadian(bb, d):
  120. return "no"
  121. if d.getVar('INHIBIT_DEFAULT_DEPS', False):
  122. # We don't have a compiler, so no icecc
  123. return "no"
  124. pn = d.getVar('PN')
  125. bpn = d.getVar('BPN')
  126. # Blacklist/whitelist checks are made against BPN, because there is a good
  127. # chance that if icecc should be skipped for a recipe, it should be skipped
  128. # for all the variants of that recipe. PN is still checked in case a user
  129. # specified a more specific recipe.
  130. check_pn = set([pn, bpn])
  131. system_class_blacklist = (d.getVar('ICECC_SYSTEM_CLASS_BL') or "").split()
  132. user_class_blacklist = (d.getVar('ICECC_USER_CLASS_BL') or "none").split()
  133. package_class_blacklist = system_class_blacklist + user_class_blacklist
  134. for black in package_class_blacklist:
  135. if bb.data.inherits_class(black, d):
  136. bb.debug(1, "%s: class %s found in blacklist, disable icecc" % (pn, black))
  137. return "no"
  138. system_package_blacklist = (d.getVar('ICECC_SYSTEM_PACKAGE_BL') or "").split()
  139. user_package_blacklist = (d.getVar('ICECC_USER_PACKAGE_BL') or "").split()
  140. user_package_whitelist = (d.getVar('ICECC_USER_PACKAGE_WL') or "").split()
  141. package_blacklist = system_package_blacklist + user_package_blacklist
  142. if check_pn & set(package_blacklist):
  143. bb.debug(1, "%s: found in blacklist, disable icecc" % pn)
  144. return "no"
  145. if check_pn & set(user_package_whitelist):
  146. bb.debug(1, "%s: found in whitelist, enable icecc" % pn)
  147. return "yes"
  148. if d.getVar('PARALLEL_MAKE') == "":
  149. bb.debug(1, "%s: has empty PARALLEL_MAKE, disable icecc" % pn)
  150. return "no"
  151. return "yes"
  152. def icecc_is_allarch(bb, d):
  153. return d.getVar("PACKAGE_ARCH") == "all"
  154. def icecc_is_kernel(bb, d):
  155. return \
  156. bb.data.inherits_class("kernel", d);
  157. def icecc_is_native(bb, d):
  158. return \
  159. bb.data.inherits_class("cross", d) or \
  160. bb.data.inherits_class("native", d);
  161. def icecc_is_cross_canadian(bb, d):
  162. return bb.data.inherits_class("cross-canadian", d)
  163. def icecc_dir(bb, d):
  164. return d.expand('${TMPDIR}/work-shared/ice')
  165. # Don't pollute allarch signatures with TARGET_FPU
  166. icecc_version[vardepsexclude] += "TARGET_FPU"
  167. def icecc_version(bb, d):
  168. if use_icecc(bb, d) == "no":
  169. return ""
  170. parallel = d.getVar('ICECC_PARALLEL_MAKE') or ""
  171. if not d.getVar('PARALLEL_MAKE') == "" and parallel:
  172. d.setVar("PARALLEL_MAKE", parallel)
  173. # Disable showing the caret in the GCC compiler output if the workaround is
  174. # disabled
  175. if d.getVar('ICECC_CARET_WORKAROUND') == '0':
  176. d.setVar('ICECC_CFLAGS', '-fno-diagnostics-show-caret')
  177. if icecc_is_native(bb, d):
  178. archive_name = "local-host-env"
  179. elif d.expand('${HOST_PREFIX}') == "":
  180. bb.fatal(d.expand("${PN}"), " NULL prefix")
  181. else:
  182. prefix = d.expand('${HOST_PREFIX}' )
  183. distro = d.expand('${DISTRO}')
  184. target_sys = d.expand('${TARGET_SYS}')
  185. float = d.getVar('TARGET_FPU') or "hard"
  186. archive_name = prefix + distro + "-" + target_sys + "-" + float
  187. if icecc_is_kernel(bb, d):
  188. archive_name += "-kernel"
  189. import socket
  190. ice_dir = icecc_dir(bb, d)
  191. tar_file = os.path.join(ice_dir, "{archive}-{version}-@VERSION@-{hostname}.tar.gz".format(
  192. archive=archive_name,
  193. version=d.getVar('ICECC_ENV_VERSION'),
  194. hostname=socket.gethostname()
  195. ))
  196. return tar_file
  197. def icecc_path(bb,d):
  198. if use_icecc(bb, d) == "no":
  199. # don't create unnecessary directories when icecc is disabled
  200. return
  201. staging = os.path.join(d.expand('${STAGING_BINDIR}'), "ice")
  202. if icecc_is_kernel(bb, d):
  203. staging += "-kernel"
  204. return staging
  205. def icecc_get_external_tool(bb, d, tool):
  206. external_toolchain_bindir = d.expand('${EXTERNAL_TOOLCHAIN}${bindir_cross}')
  207. target_prefix = d.expand('${TARGET_PREFIX}')
  208. return os.path.join(external_toolchain_bindir, '%s%s' % (target_prefix, tool))
  209. def icecc_get_tool_link(tool, d):
  210. import subprocess
  211. try:
  212. return subprocess.check_output("readlink -f %s" % tool, shell=True).decode("utf-8")[:-1]
  213. except subprocess.CalledProcessError as e:
  214. bb.note("icecc: one of the tools probably disappeared during recipe parsing, cmd readlink -f %s returned %d:\n%s" % (tool, e.returncode, e.output.decode("utf-8")))
  215. return tool
  216. def icecc_get_path_tool(tool, d):
  217. # This is a little ugly, but we want to make sure we add an actual
  218. # compiler to the toolchain, not ccache. Some distros (e.g. Fedora)
  219. # have ccache enabled by default using symlinks PATH, meaning ccache
  220. # would be found first when looking for the compiler.
  221. paths = os.getenv("PATH").split(':')
  222. while True:
  223. p, hist = bb.utils.which(':'.join(paths), tool, history=True)
  224. if not p or os.path.basename(icecc_get_tool_link(p, d)) != 'ccache':
  225. return p
  226. paths = paths[len(hist):]
  227. return ""
  228. # Don't pollute native signatures with target TUNE_PKGARCH through STAGING_BINDIR_TOOLCHAIN
  229. icecc_get_tool[vardepsexclude] += "STAGING_BINDIR_TOOLCHAIN"
  230. def icecc_get_tool(bb, d, tool):
  231. if icecc_is_native(bb, d):
  232. return icecc_get_path_tool(tool, d)
  233. elif icecc_is_kernel(bb, d):
  234. return icecc_get_path_tool(get_cross_kernel_cc(bb, d), d)
  235. else:
  236. ice_dir = d.expand('${STAGING_BINDIR_TOOLCHAIN}')
  237. target_sys = d.expand('${TARGET_SYS}')
  238. for p in ice_dir.split(':'):
  239. tool_bin = os.path.join(p, "%s-%s" % (target_sys, tool))
  240. if os.path.isfile(tool_bin):
  241. return tool_bin
  242. external_tool_bin = icecc_get_external_tool(bb, d, tool)
  243. if os.path.isfile(external_tool_bin):
  244. return external_tool_bin
  245. return ""
  246. def icecc_get_and_check_tool(bb, d, tool):
  247. # Check that g++ or gcc is not a symbolic link to icecc binary in
  248. # PATH or icecc-create-env script will silently create an invalid
  249. # compiler environment package.
  250. t = icecc_get_tool(bb, d, tool)
  251. if t:
  252. link_path = icecc_get_tool_link(t, d)
  253. if link_path == get_icecc(d):
  254. bb.error("%s is a symlink to %s in PATH and this prevents icecc from working" % (t, link_path))
  255. return ""
  256. else:
  257. return t
  258. else:
  259. return t
  260. wait_for_file() {
  261. local TIME_ELAPSED=0
  262. local FILE_TO_TEST=$1
  263. local TIMEOUT=$2
  264. until [ -f "$FILE_TO_TEST" ]
  265. do
  266. TIME_ELAPSED=`expr $TIME_ELAPSED + 1`
  267. if [ $TIME_ELAPSED -gt $TIMEOUT ]
  268. then
  269. return 1
  270. fi
  271. sleep 1
  272. done
  273. }
  274. def set_icecc_env():
  275. # dummy python version of set_icecc_env
  276. return
  277. set_icecc_env[vardepsexclude] += "KERNEL_CC"
  278. set_icecc_env() {
  279. if [ "${@use_icecc(bb, d)}" = "no" ]
  280. then
  281. return
  282. fi
  283. ICECC_VERSION="${@icecc_version(bb, d)}"
  284. if [ "x${ICECC_VERSION}" = "x" ]
  285. then
  286. bbwarn "Cannot use icecc: could not get ICECC_VERSION"
  287. return
  288. fi
  289. ICE_PATH="${@icecc_path(bb, d)}"
  290. if [ "x${ICE_PATH}" = "x" ]
  291. then
  292. bbwarn "Cannot use icecc: could not get ICE_PATH"
  293. return
  294. fi
  295. ICECC_BIN="${@get_icecc(d)}"
  296. if [ -z "${ICECC_BIN}" ]; then
  297. bbwarn "Cannot use icecc: icecc binary not found"
  298. return
  299. fi
  300. if [ -z "$(which patchelf patchelf-uninative)" ]; then
  301. bbwarn "Cannot use icecc: patchelf not found"
  302. return
  303. fi
  304. ICECC_CC="${@icecc_get_and_check_tool(bb, d, "gcc")}"
  305. ICECC_CXX="${@icecc_get_and_check_tool(bb, d, "g++")}"
  306. # cannot use icecc_get_and_check_tool here because it assumes as without target_sys prefix
  307. ICECC_WHICH_AS="${@bb.utils.which(os.getenv('PATH'), 'as')}"
  308. if [ ! -x "${ICECC_CC}" -o ! -x "${ICECC_CXX}" ]
  309. then
  310. bbwarn "Cannot use icecc: could not get ICECC_CC or ICECC_CXX"
  311. return
  312. fi
  313. ICE_VERSION=`$ICECC_CC -dumpversion`
  314. ICECC_VERSION=`echo ${ICECC_VERSION} | sed -e "s/@VERSION@/$ICE_VERSION/g"`
  315. if [ ! -x "${ICECC_ENV_EXEC}" ]
  316. then
  317. bbwarn "Cannot use icecc: invalid ICECC_ENV_EXEC"
  318. return
  319. fi
  320. # Create symlinks to icecc and wrapper-scripts in the recipe-sysroot directory
  321. mkdir -p $ICE_PATH/symlinks
  322. if [ -n "${KERNEL_CC}" ]; then
  323. compilers="${@get_cross_kernel_cc(bb,d)}"
  324. else
  325. compilers="${HOST_PREFIX}gcc ${HOST_PREFIX}g++"
  326. fi
  327. for compiler in $compilers; do
  328. ln -sf $ICECC_BIN $ICE_PATH/symlinks/$compiler
  329. rm -f $ICE_PATH/$compiler
  330. cat <<-__EOF__ > $ICE_PATH/$compiler
  331. #!/bin/sh -e
  332. export ICECC_VERSION=$ICECC_VERSION
  333. export ICECC_CC=$ICECC_CC
  334. export ICECC_CXX=$ICECC_CXX
  335. $ICE_PATH/symlinks/$compiler "\$@"
  336. __EOF__
  337. chmod 775 $ICE_PATH/$compiler
  338. done
  339. ICECC_AS="`${ICECC_CC} -print-prog-name=as`"
  340. # for target recipes should return something like:
  341. # /OE/tmp-eglibc/sysroots/x86_64-linux/usr/libexec/arm920tt-oe-linux-gnueabi/gcc/arm-oe-linux-gnueabi/4.8.2/as
  342. # and just "as" for native, if it returns "as" in current directory (for whatever reason) use "as" from PATH
  343. if [ "`dirname "${ICECC_AS}"`" = "." ]
  344. then
  345. ICECC_AS="${ICECC_WHICH_AS}"
  346. fi
  347. if [ ! -f "${ICECC_VERSION}.done" ]
  348. then
  349. mkdir -p "`dirname "${ICECC_VERSION}"`"
  350. # the ICECC_VERSION generation step must be locked by a mutex
  351. # in order to prevent race conditions
  352. if flock -n "${ICECC_VERSION}.lock" \
  353. ${ICECC_ENV_EXEC} ${ICECC_ENV_DEBUG} "${ICECC_CC}" "${ICECC_CXX}" "${ICECC_AS}" "${ICECC_VERSION}"
  354. then
  355. touch "${ICECC_VERSION}.done"
  356. elif ! wait_for_file "${ICECC_VERSION}.done" 30
  357. then
  358. # locking failed so wait for ${ICECC_VERSION}.done to appear
  359. bbwarn "Timeout waiting for ${ICECC_VERSION}.done"
  360. return
  361. fi
  362. fi
  363. # Don't let ccache find the icecream compiler links that have been created, otherwise
  364. # it can end up invoking icecream recursively.
  365. export CCACHE_PATH="$PATH"
  366. export CCACHE_DISABLE="1"
  367. export PATH="$ICE_PATH:$PATH"
  368. bbnote "Using icecc path: $ICE_PATH"
  369. bbnote "Using icecc tarball: $ICECC_VERSION"
  370. }
  371. do_configure_prepend() {
  372. set_icecc_env
  373. }
  374. do_compile_prepend() {
  375. set_icecc_env
  376. }
  377. do_compile_kernelmodules_prepend() {
  378. set_icecc_env
  379. }
  380. do_install_prepend() {
  381. set_icecc_env
  382. }
  383. # IceCream is not (currently) supported in the extensible SDK
  384. ICECC_SDK_HOST_TASK = "nativesdk-icecc-toolchain"
  385. ICECC_SDK_HOST_TASK_task-populate-sdk-ext = ""
  386. # Don't include IceCream in uninative tarball
  387. ICECC_SDK_HOST_TASK_pn-uninative-tarball = ""
  388. # Add the toolchain scripts to the SDK
  389. TOOLCHAIN_HOST_TASK_append = " ${ICECC_SDK_HOST_TASK}"