icecc.bbclass 16 KB

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