summaryrefslogtreecommitdiffstats
path: root/meta/classes/icecc.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/icecc.bbclass')
-rw-r--r--meta/classes/icecc.bbclass325
1 files changed, 325 insertions, 0 deletions
diff --git a/meta/classes/icecc.bbclass b/meta/classes/icecc.bbclass
new file mode 100644
index 0000000..5c9e66c
--- /dev/null
+++ b/meta/classes/icecc.bbclass
@@ -0,0 +1,325 @@
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
31BB_HASHBASE_WHITELIST += "ICECC_PARALLEL_MAKE ICECC_DISABLED ICECC_USER_PACKAGE_BL ICECC_USER_CLASS_BL ICECC_USER_PACKAGE_WL"
32
33ICECC_ENV_EXEC ?= "${STAGING_BINDIR_NATIVE}/icecc-create-env"
34
35def icecc_dep_prepend(d):
36 # INHIBIT_DEFAULT_DEPS doesn't apply to the patch command. Whether or not
37 # we need that built is the responsibility of the patch function / class, not
38 # the application.
39 if not d.getVar('INHIBIT_DEFAULT_DEPS'):
40 return "icecc-create-env-native"
41 return ""
42
43DEPENDS_prepend += "${@icecc_dep_prepend(d)} "
44
45def get_cross_kernel_cc(bb,d):
46 kernel_cc = d.getVar('KERNEL_CC')
47
48 # evaluate the expression by the shell if necessary
49 if '`' in kernel_cc or '$(' in kernel_cc:
50 kernel_cc = os.popen("echo %s" % kernel_cc).read()[:-1]
51
52 kernel_cc = d.expand(kernel_cc)
53 kernel_cc = kernel_cc.replace('ccache', '').strip()
54 kernel_cc = kernel_cc.split(' ')[0]
55 kernel_cc = kernel_cc.strip()
56 return kernel_cc
57
58def get_icecc(d):
59 return d.getVar('ICECC_PATH') or bb.utils.which(os.getenv("PATH"), "icecc")
60
61def create_path(compilers, bb, d):
62 """
63 Create Symlinks for the icecc in the staging directory
64 """
65 staging = os.path.join(d.expand('${STAGING_BINDIR}'), "ice")
66 if icc_is_kernel(bb, d):
67 staging += "-kernel"
68
69 #check if the icecc path is set by the user
70 icecc = get_icecc(d)
71
72 # Create the dir if necessary
73 try:
74 os.stat(staging)
75 except:
76 try:
77 os.makedirs(staging)
78 except:
79 pass
80
81 for compiler in compilers:
82 gcc_path = os.path.join(staging, compiler)
83 try:
84 os.stat(gcc_path)
85 except:
86 try:
87 os.symlink(icecc, gcc_path)
88 except:
89 pass
90
91 return staging
92
93def use_icc(bb,d):
94 # allarch recipes don't use compiler
95 if icc_is_allarch(bb, d):
96 return "no"
97
98 pn = d.getVar('PN', True)
99
100 system_class_blacklist = []
101 user_class_blacklist = (d.getVar('ICECC_USER_CLASS_BL') or "none").split()
102 package_class_blacklist = system_class_blacklist + user_class_blacklist
103
104 for black in package_class_blacklist:
105 if bb.data.inherits_class(black, d):
106 bb.debug(1, "%s: class %s found in blacklist, disable icecc" % (pn, black))
107 return "no"
108
109 # "system" recipe blacklist contains a list of packages that can not distribute compile tasks
110 # for one reason or the other
111 # this is the old list (which doesn't seem to be valid anymore, because I was able to build
112 # all these with icecc enabled)
113 # system_package_blacklist = [ "uclibc", "glibc", "gcc", "bind", "u-boot", "dhcp-forwarder", "enchant", "connman", "orbit2" ]
114 # when adding new entry, please document why (how it failed) so that we can re-evaluate it later
115 # e.g. when there is new version
116 system_package_blacklist = []
117 user_package_blacklist = (d.getVar('ICECC_USER_PACKAGE_BL') or "").split()
118 user_package_whitelist = (d.getVar('ICECC_USER_PACKAGE_WL') or "").split()
119 package_blacklist = system_package_blacklist + user_package_blacklist
120
121 if pn in package_blacklist:
122 bb.debug(1, "%s: found in blacklist, disable icecc" % pn)
123 return "no"
124
125 if pn in user_package_whitelist:
126 bb.debug(1, "%s: found in whitelist, enable icecc" % pn)
127 return "yes"
128
129 if d.getVar('PARALLEL_MAKE') == "":
130 bb.debug(1, "%s: has empty PARALLEL_MAKE, disable icecc" % pn)
131 return "no"
132
133 return "yes"
134
135def icc_is_allarch(bb, d):
136 return \
137 bb.data.inherits_class("allarch", d);
138
139def icc_is_kernel(bb, d):
140 return \
141 bb.data.inherits_class("kernel", d);
142
143def icc_is_native(bb, d):
144 return \
145 bb.data.inherits_class("cross", d) or \
146 bb.data.inherits_class("native", d);
147
148# Don't pollute allarch signatures with TARGET_FPU
149icc_version[vardepsexclude] += "TARGET_FPU"
150def icc_version(bb, d):
151 if use_icc(bb, d) == "no":
152 return ""
153
154 parallel = d.getVar('ICECC_PARALLEL_MAKE') or ""
155 if not d.getVar('PARALLEL_MAKE') == "" and parallel:
156 d.setVar("PARALLEL_MAKE", parallel)
157
158 if icc_is_native(bb, d):
159 archive_name = "local-host-env"
160 elif d.expand('${HOST_PREFIX}') == "":
161 bb.fatal(d.expand("${PN}"), " NULL prefix")
162 else:
163 prefix = d.expand('${HOST_PREFIX}' )
164 distro = d.expand('${DISTRO}')
165 target_sys = d.expand('${TARGET_SYS}')
166 float = d.getVar('TARGET_FPU') or "hard"
167 archive_name = prefix + distro + "-" + target_sys + "-" + float
168 if icc_is_kernel(bb, d):
169 archive_name += "-kernel"
170
171 import socket
172 ice_dir = d.expand('${STAGING_DIR_NATIVE}${prefix_native}')
173 tar_file = os.path.join(ice_dir, 'ice', archive_name + "-@VERSION@-" + socket.gethostname() + '.tar.gz')
174
175 return tar_file
176
177def icc_path(bb,d):
178 if icc_is_kernel(bb, d):
179 return create_path( [get_cross_kernel_cc(bb,d), ], bb, d)
180
181 else:
182 prefix = d.expand('${HOST_PREFIX}')
183 return create_path( [prefix+"gcc", prefix+"g++"], bb, d)
184
185def icc_get_external_tool(bb, d, tool):
186 external_toolchain_bindir = d.expand('${EXTERNAL_TOOLCHAIN}${bindir_cross}')
187 target_prefix = d.expand('${TARGET_PREFIX}')
188 return os.path.join(external_toolchain_bindir, '%s%s' % (target_prefix, tool))
189
190# Don't pollute native signatures with target TUNE_PKGARCH through STAGING_BINDIR_TOOLCHAIN
191icc_get_tool[vardepsexclude] += "STAGING_BINDIR_TOOLCHAIN"
192def icc_get_tool(bb, d, tool):
193 if icc_is_native(bb, d):
194 return bb.utils.which(os.getenv("PATH"), tool)
195 elif icc_is_kernel(bb, d):
196 return bb.utils.which(os.getenv("PATH"), get_cross_kernel_cc(bb, d))
197 else:
198 ice_dir = d.expand('${STAGING_BINDIR_TOOLCHAIN}')
199 target_sys = d.expand('${TARGET_SYS}')
200 tool_bin = os.path.join(ice_dir, "%s-%s" % (target_sys, tool))
201 if os.path.isfile(tool_bin):
202 return tool_bin
203 else:
204 external_tool_bin = icc_get_external_tool(bb, d, tool)
205 if os.path.isfile(external_tool_bin):
206 return external_tool_bin
207 else:
208 return ""
209
210def icc_get_and_check_tool(bb, d, tool):
211 # Check that g++ or gcc is not a symbolic link to icecc binary in
212 # PATH or icecc-create-env script will silently create an invalid
213 # compiler environment package.
214 t = icc_get_tool(bb, d, tool)
215 if t and os.popen("readlink -f %s" % t).read()[:-1] == get_icecc(d):
216 bb.error("%s is a symlink to %s in PATH and this prevents icecc from working" % (t, get_icecc(d)))
217 return ""
218 else:
219 return t
220
221wait_for_file() {
222 local TIME_ELAPSED=0
223 local FILE_TO_TEST=$1
224 local TIMEOUT=$2
225 until [ -f "$FILE_TO_TEST" ]
226 do
227 TIME_ELAPSED=`expr $TIME_ELAPSED + 1`
228 if [ $TIME_ELAPSED -gt $TIMEOUT ]
229 then
230 return 1
231 fi
232 sleep 1
233 done
234}
235
236def set_icecc_env():
237 # dummy python version of set_icecc_env
238 return
239
240set_icecc_env() {
241 if [ "x${ICECC_DISABLED}" != "x" ]
242 then
243 return
244 fi
245 ICECC_VERSION="${@icc_version(bb, d)}"
246 if [ "x${ICECC_VERSION}" = "x" ]
247 then
248 bbwarn "Cannot use icecc: could not get ICECC_VERSION"
249 return
250 fi
251
252 ICE_PATH="${@icc_path(bb, d)}"
253 if [ "x${ICE_PATH}" = "x" ]
254 then
255 bbwarn "Cannot use icecc: could not get ICE_PATH"
256 return
257 fi
258
259 ICECC_CC="${@icc_get_and_check_tool(bb, d, "gcc")}"
260 ICECC_CXX="${@icc_get_and_check_tool(bb, d, "g++")}"
261 # cannot use icc_get_and_check_tool here because it assumes as without target_sys prefix
262 ICECC_WHICH_AS="${@bb.utils.which(os.getenv('PATH'), 'as')}"
263 if [ ! -x "${ICECC_CC}" -o ! -x "${ICECC_CXX}" ]
264 then
265 bbwarn "Cannot use icecc: could not get ICECC_CC or ICECC_CXX"
266 return
267 fi
268
269 ICE_VERSION=`$ICECC_CC -dumpversion`
270 ICECC_VERSION=`echo ${ICECC_VERSION} | sed -e "s/@VERSION@/$ICE_VERSION/g"`
271 if [ ! -x "${ICECC_ENV_EXEC}" ]
272 then
273 bbwarn "Cannot use icecc: invalid ICECC_ENV_EXEC"
274 return
275 fi
276
277 ICECC_AS="`${ICECC_CC} -print-prog-name=as`"
278 # for target recipes should return something like:
279 # /OE/tmp-eglibc/sysroots/x86_64-linux/usr/libexec/arm920tt-oe-linux-gnueabi/gcc/arm-oe-linux-gnueabi/4.8.2/as
280 # and just "as" for native, if it returns "as" in current directory (for whatever reason) use "as" from PATH
281 if [ "`dirname "${ICECC_AS}"`" = "." ]
282 then
283 ICECC_AS="${ICECC_WHICH_AS}"
284 fi
285
286 if [ ! -f "${ICECC_VERSION}.done" ]
287 then
288 mkdir -p "`dirname "${ICECC_VERSION}"`"
289
290 # the ICECC_VERSION generation step must be locked by a mutex
291 # in order to prevent race conditions
292 if flock -n "${ICECC_VERSION}.lock" \
293 ${ICECC_ENV_EXEC} "${ICECC_CC}" "${ICECC_CXX}" "${ICECC_AS}" "${ICECC_VERSION}"
294 then
295 touch "${ICECC_VERSION}.done"
296 elif [ ! wait_for_file "${ICECC_VERSION}.done" 30 ]
297 then
298 # locking failed so wait for ${ICECC_VERSION}.done to appear
299 bbwarn "Timeout waiting for ${ICECC_VERSION}.done"
300 return
301 fi
302 fi
303
304 export ICECC_VERSION ICECC_CC ICECC_CXX
305 export PATH="$ICE_PATH:$PATH"
306 export CCACHE_PATH="$PATH"
307
308 bbnote "Using icecc"
309}
310
311do_configure_prepend() {
312 set_icecc_env
313}
314
315do_compile_prepend() {
316 set_icecc_env
317}
318
319do_compile_kernelmodules_prepend() {
320 set_icecc_env
321}
322
323do_install_prepend() {
324 set_icecc_env
325}