diff options
Diffstat (limited to 'meta/classes/insane.bbclass')
-rw-r--r-- | meta/classes/insane.bbclass | 1153 |
1 files changed, 1153 insertions, 0 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass new file mode 100644 index 0000000000..c6dea22618 --- /dev/null +++ b/meta/classes/insane.bbclass | |||
@@ -0,0 +1,1153 @@ | |||
1 | # BB Class inspired by ebuild.sh | ||
2 | # | ||
3 | # This class will test files after installation for certain | ||
4 | # security issues and other kind of issues. | ||
5 | # | ||
6 | # Checks we do: | ||
7 | # -Check the ownership and permissions | ||
8 | # -Check the RUNTIME path for the $TMPDIR | ||
9 | # -Check if .la files wrongly point to workdir | ||
10 | # -Check if .pc files wrongly point to workdir | ||
11 | # -Check if packages contains .debug directories or .so files | ||
12 | # where they should be in -dev or -dbg | ||
13 | # -Check if config.log contains traces to broken autoconf tests | ||
14 | # -Ensure that binaries in base_[bindir|sbindir|libdir] do not link | ||
15 | # into exec_prefix | ||
16 | # -Check that scripts in base_[bindir|sbindir|libdir] do not reference | ||
17 | # files under exec_prefix | ||
18 | |||
19 | |||
20 | # unsafe-references-in-binaries requires prelink-rtld from | ||
21 | # prelink-native, but we don't want this DEPENDS for -native builds | ||
22 | QADEPENDS = "prelink-native" | ||
23 | QADEPENDS_class-native = "" | ||
24 | QADEPENDS_class-nativesdk = "" | ||
25 | QA_SANE = "True" | ||
26 | |||
27 | # Elect whether a given type of error is a warning or error, they may | ||
28 | # have been set by other files. | ||
29 | WARN_QA ?= "ldflags useless-rpaths rpaths staticdev libdir xorg-driver-abi \ | ||
30 | textrel already-stripped incompatible-license files-invalid \ | ||
31 | installed-vs-shipped compile-host-path install-host-path \ | ||
32 | pn-overrides infodir build-deps file-rdeps \ | ||
33 | " | ||
34 | ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch pkgconfig la \ | ||
35 | perms dep-cmp pkgvarcheck perm-config perm-line perm-link \ | ||
36 | split-strip packages-list pkgv-undefined var-undefined \ | ||
37 | version-going-backwards \ | ||
38 | " | ||
39 | |||
40 | ALL_QA = "${WARN_QA} ${ERROR_QA}" | ||
41 | |||
42 | UNKNOWN_CONFIGURE_WHITELIST ?= "--enable-nls --disable-nls --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot" | ||
43 | |||
44 | # | ||
45 | # dictionary for elf headers | ||
46 | # | ||
47 | # feel free to add and correct. | ||
48 | # | ||
49 | # TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit? | ||
50 | def package_qa_get_machine_dict(): | ||
51 | return { | ||
52 | "darwin9" : { | ||
53 | "arm" : (40, 0, 0, True, 32), | ||
54 | }, | ||
55 | "linux" : { | ||
56 | "aarch64" : (183, 0, 0, True, 64), | ||
57 | "aarch64_be" :(183, 0, 0, False, 64), | ||
58 | "arm" : (40, 97, 0, True, 32), | ||
59 | "armeb": (40, 97, 0, False, 32), | ||
60 | "powerpc": (20, 0, 0, False, 32), | ||
61 | "powerpc64": (21, 0, 0, False, 64), | ||
62 | "i386": ( 3, 0, 0, True, 32), | ||
63 | "i486": ( 3, 0, 0, True, 32), | ||
64 | "i586": ( 3, 0, 0, True, 32), | ||
65 | "i686": ( 3, 0, 0, True, 32), | ||
66 | "x86_64": (62, 0, 0, True, 64), | ||
67 | "ia64": (50, 0, 0, True, 64), | ||
68 | "alpha": (36902, 0, 0, True, 64), | ||
69 | "hppa": (15, 3, 0, False, 32), | ||
70 | "m68k": ( 4, 0, 0, False, 32), | ||
71 | "mips": ( 8, 0, 0, False, 32), | ||
72 | "mipsel": ( 8, 0, 0, True, 32), | ||
73 | "mips64": ( 8, 0, 0, False, 64), | ||
74 | "mips64el": ( 8, 0, 0, True, 64), | ||
75 | "s390": (22, 0, 0, False, 32), | ||
76 | "sh4": (42, 0, 0, True, 32), | ||
77 | "sparc": ( 2, 0, 0, False, 32), | ||
78 | "microblaze": (189, 0, 0, False, 32), | ||
79 | "microblazeel":(189, 0, 0, True, 32), | ||
80 | }, | ||
81 | "linux-uclibc" : { | ||
82 | "arm" : ( 40, 97, 0, True, 32), | ||
83 | "armeb": ( 40, 97, 0, False, 32), | ||
84 | "powerpc": ( 20, 0, 0, False, 32), | ||
85 | "i386": ( 3, 0, 0, True, 32), | ||
86 | "i486": ( 3, 0, 0, True, 32), | ||
87 | "i586": ( 3, 0, 0, True, 32), | ||
88 | "i686": ( 3, 0, 0, True, 32), | ||
89 | "x86_64": ( 62, 0, 0, True, 64), | ||
90 | "mips": ( 8, 0, 0, False, 32), | ||
91 | "mipsel": ( 8, 0, 0, True, 32), | ||
92 | "mips64": ( 8, 0, 0, False, 64), | ||
93 | "mips64el": ( 8, 0, 0, True, 64), | ||
94 | "avr32": (6317, 0, 0, False, 32), | ||
95 | "sh4": (42, 0, 0, True, 32), | ||
96 | |||
97 | }, | ||
98 | "linux-musl" : { | ||
99 | "arm" : ( 40, 97, 0, True, 32), | ||
100 | "armeb": ( 40, 97, 0, False, 32), | ||
101 | "powerpc": ( 20, 0, 0, False, 32), | ||
102 | "i386": ( 3, 0, 0, True, 32), | ||
103 | "i486": ( 3, 0, 0, True, 32), | ||
104 | "i586": ( 3, 0, 0, True, 32), | ||
105 | "i686": ( 3, 0, 0, True, 32), | ||
106 | "x86_64": ( 62, 0, 0, True, 64), | ||
107 | "mips": ( 8, 0, 0, False, 32), | ||
108 | "mipsel": ( 8, 0, 0, True, 32), | ||
109 | "mips64": ( 8, 0, 0, False, 64), | ||
110 | "mips64el": ( 8, 0, 0, True, 64), | ||
111 | }, | ||
112 | "uclinux-uclibc" : { | ||
113 | "bfin": ( 106, 0, 0, True, 32), | ||
114 | }, | ||
115 | "linux-gnueabi" : { | ||
116 | "arm" : (40, 0, 0, True, 32), | ||
117 | "armeb" : (40, 0, 0, False, 32), | ||
118 | }, | ||
119 | "linux-musleabi" : { | ||
120 | "arm" : (40, 0, 0, True, 32), | ||
121 | "armeb" : (40, 0, 0, False, 32), | ||
122 | }, | ||
123 | "linux-uclibceabi" : { | ||
124 | "arm" : (40, 0, 0, True, 32), | ||
125 | "armeb" : (40, 0, 0, False, 32), | ||
126 | }, | ||
127 | "linux-gnuspe" : { | ||
128 | "powerpc": (20, 0, 0, False, 32), | ||
129 | }, | ||
130 | "linux-muslspe" : { | ||
131 | "powerpc": (20, 0, 0, False, 32), | ||
132 | }, | ||
133 | "linux-uclibcspe" : { | ||
134 | "powerpc": (20, 0, 0, False, 32), | ||
135 | }, | ||
136 | "linux-gnu" : { | ||
137 | "powerpc": (20, 0, 0, False, 32), | ||
138 | "sh4": (42, 0, 0, True, 32), | ||
139 | }, | ||
140 | "linux-gnux32" : { | ||
141 | "x86_64": (62, 0, 0, True, 32), | ||
142 | }, | ||
143 | "linux-gnun32" : { | ||
144 | "mips64": ( 8, 0, 0, False, 32), | ||
145 | "mips64el": ( 8, 0, 0, True, 32), | ||
146 | }, | ||
147 | } | ||
148 | |||
149 | |||
150 | def package_qa_clean_path(path,d): | ||
151 | """ Remove the common prefix from the path. In this case it is the TMPDIR""" | ||
152 | return path.replace(d.getVar('TMPDIR',True),"") | ||
153 | |||
154 | def package_qa_write_error(type, error, d): | ||
155 | logfile = d.getVar('QA_LOGFILE', True) | ||
156 | if logfile: | ||
157 | p = d.getVar('P', True) | ||
158 | f = file( logfile, "a+") | ||
159 | print >> f, "%s: %s [%s]" % (p, error, type) | ||
160 | f.close() | ||
161 | |||
162 | def package_qa_handle_error(error_class, error_msg, d): | ||
163 | package_qa_write_error(error_class, error_msg, d) | ||
164 | if error_class in (d.getVar("ERROR_QA", True) or "").split(): | ||
165 | bb.error("QA Issue: %s [%s]" % (error_msg, error_class)) | ||
166 | d.setVar("QA_SANE", False) | ||
167 | return False | ||
168 | elif error_class in (d.getVar("WARN_QA", True) or "").split(): | ||
169 | bb.warn("QA Issue: %s [%s]" % (error_msg, error_class)) | ||
170 | else: | ||
171 | bb.note("QA Issue: %s [%s]" % (error_msg, error_class)) | ||
172 | return True | ||
173 | |||
174 | QAPATHTEST[libexec] = "package_qa_check_libexec" | ||
175 | def package_qa_check_libexec(path,name, d, elf, messages): | ||
176 | |||
177 | # Skip the case where the default is explicitly /usr/libexec | ||
178 | libexec = d.getVar('libexecdir', True) | ||
179 | if libexec == "/usr/libexec": | ||
180 | return True | ||
181 | |||
182 | if 'libexec' in path.split(os.path.sep): | ||
183 | messages["libexec"] = "%s: %s is using libexec please relocate to %s" % (name, package_qa_clean_path(path, d), libexec) | ||
184 | return False | ||
185 | |||
186 | return True | ||
187 | |||
188 | QAPATHTEST[rpaths] = "package_qa_check_rpath" | ||
189 | def package_qa_check_rpath(file,name, d, elf, messages): | ||
190 | """ | ||
191 | Check for dangerous RPATHs | ||
192 | """ | ||
193 | if not elf: | ||
194 | return | ||
195 | |||
196 | if os.path.islink(file): | ||
197 | return | ||
198 | |||
199 | bad_dirs = [d.getVar('BASE_WORKDIR', True), d.getVar('STAGING_DIR_TARGET', True)] | ||
200 | |||
201 | phdrs = elf.run_objdump("-p", d) | ||
202 | |||
203 | import re | ||
204 | rpath_re = re.compile("\s+RPATH\s+(.*)") | ||
205 | for line in phdrs.split("\n"): | ||
206 | m = rpath_re.match(line) | ||
207 | if m: | ||
208 | rpath = m.group(1) | ||
209 | for dir in bad_dirs: | ||
210 | if dir in rpath: | ||
211 | messages["rpaths"] = "package %s contains bad RPATH %s in file %s" % (name, rpath, file) | ||
212 | |||
213 | QAPATHTEST[useless-rpaths] = "package_qa_check_useless_rpaths" | ||
214 | def package_qa_check_useless_rpaths(file, name, d, elf, messages): | ||
215 | """ | ||
216 | Check for RPATHs that are useless but not dangerous | ||
217 | """ | ||
218 | def rpath_eq(a, b): | ||
219 | return os.path.normpath(a) == os.path.normpath(b) | ||
220 | |||
221 | if not elf: | ||
222 | return | ||
223 | |||
224 | if os.path.islink(file): | ||
225 | return | ||
226 | |||
227 | libdir = d.getVar("libdir", True) | ||
228 | base_libdir = d.getVar("base_libdir", True) | ||
229 | |||
230 | phdrs = elf.run_objdump("-p", d) | ||
231 | |||
232 | import re | ||
233 | rpath_re = re.compile("\s+RPATH\s+(.*)") | ||
234 | for line in phdrs.split("\n"): | ||
235 | m = rpath_re.match(line) | ||
236 | if m: | ||
237 | rpath = m.group(1) | ||
238 | if rpath_eq(rpath, libdir) or rpath_eq(rpath, base_libdir): | ||
239 | # The dynamic linker searches both these places anyway. There is no point in | ||
240 | # looking there again. | ||
241 | messages["useless-rpaths"] = "%s: %s contains probably-redundant RPATH %s" % (name, package_qa_clean_path(file, d), rpath) | ||
242 | |||
243 | QAPATHTEST[dev-so] = "package_qa_check_dev" | ||
244 | def package_qa_check_dev(path, name, d, elf, messages): | ||
245 | """ | ||
246 | Check for ".so" library symlinks in non-dev packages | ||
247 | """ | ||
248 | |||
249 | if not name.endswith("-dev") and not name.endswith("-dbg") and not name.endswith("-ptest") and not name.startswith("nativesdk-") and path.endswith(".so") and os.path.islink(path): | ||
250 | messages["dev-so"] = "non -dev/-dbg/-nativesdk package contains symlink .so: %s path '%s'" % \ | ||
251 | (name, package_qa_clean_path(path,d)) | ||
252 | |||
253 | QAPATHTEST[staticdev] = "package_qa_check_staticdev" | ||
254 | def package_qa_check_staticdev(path, name, d, elf, messages): | ||
255 | """ | ||
256 | Check for ".a" library in non-staticdev packages | ||
257 | There are a number of exceptions to this rule, -pic packages can contain | ||
258 | static libraries, the _nonshared.a belong with their -dev packages and | ||
259 | libgcc.a, libgcov.a will be skipped in their packages | ||
260 | """ | ||
261 | |||
262 | if not name.endswith("-pic") and not name.endswith("-staticdev") and not name.endswith("-ptest") and path.endswith(".a") and not path.endswith("_nonshared.a"): | ||
263 | messages["staticdev"] = "non -staticdev package contains static .a library: %s path '%s'" % \ | ||
264 | (name, package_qa_clean_path(path,d)) | ||
265 | |||
266 | def package_qa_check_libdir(d): | ||
267 | """ | ||
268 | Check for wrong library installation paths. For instance, catch | ||
269 | recipes installing /lib/bar.so when ${base_libdir}="lib32" or | ||
270 | installing in /usr/lib64 when ${libdir}="/usr/lib" | ||
271 | """ | ||
272 | import re | ||
273 | |||
274 | pkgdest = d.getVar('PKGDEST', True) | ||
275 | base_libdir = d.getVar("base_libdir",True) + os.sep | ||
276 | libdir = d.getVar("libdir", True) + os.sep | ||
277 | exec_prefix = d.getVar("exec_prefix", True) + os.sep | ||
278 | |||
279 | messages = [] | ||
280 | |||
281 | lib_re = re.compile("^/lib.+\.so(\..+)?$") | ||
282 | exec_re = re.compile("^%s.*/lib.+\.so(\..+)?$" % exec_prefix) | ||
283 | |||
284 | for root, dirs, files in os.walk(pkgdest): | ||
285 | if root == pkgdest: | ||
286 | # Skip subdirectories for any packages with libdir in INSANE_SKIP | ||
287 | skippackages = [] | ||
288 | for package in dirs: | ||
289 | if 'libdir' in (d.getVar('INSANE_SKIP_' + package, True) or "").split(): | ||
290 | bb.note("Package %s skipping libdir QA test" % (package)) | ||
291 | skippackages.append(package) | ||
292 | for package in skippackages: | ||
293 | dirs.remove(package) | ||
294 | for file in files: | ||
295 | full_path = os.path.join(root, file) | ||
296 | rel_path = os.path.relpath(full_path, pkgdest) | ||
297 | if os.sep in rel_path: | ||
298 | package, rel_path = rel_path.split(os.sep, 1) | ||
299 | rel_path = os.sep + rel_path | ||
300 | if lib_re.match(rel_path): | ||
301 | if base_libdir not in rel_path: | ||
302 | messages.append("%s: found library in wrong location: %s" % (package, rel_path)) | ||
303 | if exec_re.match(rel_path): | ||
304 | if libdir not in rel_path: | ||
305 | messages.append("%s: found library in wrong location: %s" % (package, rel_path)) | ||
306 | |||
307 | if messages: | ||
308 | package_qa_handle_error("libdir", "\n".join(messages), d) | ||
309 | |||
310 | QAPATHTEST[debug-files] = "package_qa_check_dbg" | ||
311 | def package_qa_check_dbg(path, name, d, elf, messages): | ||
312 | """ | ||
313 | Check for ".debug" files or directories outside of the dbg package | ||
314 | """ | ||
315 | |||
316 | if not "-dbg" in name and not "-ptest" in name: | ||
317 | if '.debug' in path.split(os.path.sep): | ||
318 | messages["debug-files"] = "non debug package contains .debug directory: %s path %s" % \ | ||
319 | (name, package_qa_clean_path(path,d)) | ||
320 | |||
321 | QAPATHTEST[perms] = "package_qa_check_perm" | ||
322 | def package_qa_check_perm(path,name,d, elf, messages): | ||
323 | """ | ||
324 | Check the permission of files | ||
325 | """ | ||
326 | return | ||
327 | |||
328 | QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries" | ||
329 | def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages): | ||
330 | """ | ||
331 | Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix | ||
332 | """ | ||
333 | if unsafe_references_skippable(path, name, d): | ||
334 | return | ||
335 | |||
336 | if elf: | ||
337 | import subprocess as sub | ||
338 | pn = d.getVar('PN', True) | ||
339 | |||
340 | exec_prefix = d.getVar('exec_prefix', True) | ||
341 | sysroot_path = d.getVar('STAGING_DIR_TARGET', True) | ||
342 | sysroot_path_usr = sysroot_path + exec_prefix | ||
343 | |||
344 | try: | ||
345 | ldd_output = bb.process.Popen(["prelink-rtld", "--root", sysroot_path, path], stdout=sub.PIPE).stdout.read() | ||
346 | except bb.process.CmdError: | ||
347 | error_msg = pn + ": prelink-rtld aborted when processing %s" % path | ||
348 | package_qa_handle_error("unsafe-references-in-binaries", error_msg, d) | ||
349 | return False | ||
350 | |||
351 | if sysroot_path_usr in ldd_output: | ||
352 | ldd_output = ldd_output.replace(sysroot_path, "") | ||
353 | |||
354 | pkgdest = d.getVar('PKGDEST', True) | ||
355 | packages = d.getVar('PACKAGES', True) | ||
356 | |||
357 | for package in packages.split(): | ||
358 | short_path = path.replace('%s/%s' % (pkgdest, package), "", 1) | ||
359 | if (short_path != path): | ||
360 | break | ||
361 | |||
362 | base_err = pn + ": %s, installed in the base_prefix, requires a shared library under exec_prefix (%s)" % (short_path, exec_prefix) | ||
363 | for line in ldd_output.split('\n'): | ||
364 | if exec_prefix in line: | ||
365 | error_msg = "%s: %s" % (base_err, line.strip()) | ||
366 | package_qa_handle_error("unsafe-references-in-binaries", error_msg, d) | ||
367 | |||
368 | return False | ||
369 | |||
370 | QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts" | ||
371 | def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages): | ||
372 | """ | ||
373 | Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix | ||
374 | """ | ||
375 | if unsafe_references_skippable(path, name, d): | ||
376 | return | ||
377 | |||
378 | if not elf: | ||
379 | import stat | ||
380 | import subprocess | ||
381 | pn = d.getVar('PN', True) | ||
382 | |||
383 | # Ensure we're checking an executable script | ||
384 | statinfo = os.stat(path) | ||
385 | if bool(statinfo.st_mode & stat.S_IXUSR): | ||
386 | # grep shell scripts for possible references to /exec_prefix/ | ||
387 | exec_prefix = d.getVar('exec_prefix', True) | ||
388 | statement = "grep -e '%s/' %s > /dev/null" % (exec_prefix, path) | ||
389 | if subprocess.call(statement, shell=True) == 0: | ||
390 | error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path) | ||
391 | package_qa_handle_error("unsafe-references-in-scripts", error_msg, d) | ||
392 | error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix" | ||
393 | package_qa_handle_error("unsafe-references-in-scripts", error_msg, d) | ||
394 | |||
395 | def unsafe_references_skippable(path, name, d): | ||
396 | if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d): | ||
397 | return True | ||
398 | |||
399 | if "-dbg" in name or "-dev" in name: | ||
400 | return True | ||
401 | |||
402 | # Other package names to skip: | ||
403 | if name.startswith("kernel-module-"): | ||
404 | return True | ||
405 | |||
406 | # Skip symlinks | ||
407 | if os.path.islink(path): | ||
408 | return True | ||
409 | |||
410 | # Skip unusual rootfs layouts which make these tests irrelevant | ||
411 | exec_prefix = d.getVar('exec_prefix', True) | ||
412 | if exec_prefix == "": | ||
413 | return True | ||
414 | |||
415 | pkgdest = d.getVar('PKGDEST', True) | ||
416 | pkgdest = pkgdest + "/" + name | ||
417 | pkgdest = os.path.abspath(pkgdest) | ||
418 | base_bindir = pkgdest + d.getVar('base_bindir', True) | ||
419 | base_sbindir = pkgdest + d.getVar('base_sbindir', True) | ||
420 | base_libdir = pkgdest + d.getVar('base_libdir', True) | ||
421 | bindir = pkgdest + d.getVar('bindir', True) | ||
422 | sbindir = pkgdest + d.getVar('sbindir', True) | ||
423 | libdir = pkgdest + d.getVar('libdir', True) | ||
424 | |||
425 | if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir: | ||
426 | return True | ||
427 | |||
428 | # Skip files not in base_[bindir|sbindir|libdir] | ||
429 | path = os.path.abspath(path) | ||
430 | if not (base_bindir in path or base_sbindir in path or base_libdir in path): | ||
431 | return True | ||
432 | |||
433 | return False | ||
434 | |||
435 | QAPATHTEST[arch] = "package_qa_check_arch" | ||
436 | def package_qa_check_arch(path,name,d, elf, messages): | ||
437 | """ | ||
438 | Check if archs are compatible | ||
439 | """ | ||
440 | if not elf: | ||
441 | return | ||
442 | |||
443 | target_os = d.getVar('TARGET_OS', True) | ||
444 | target_arch = d.getVar('TARGET_ARCH', True) | ||
445 | provides = d.getVar('PROVIDES', True) | ||
446 | bpn = d.getVar('BPN', True) | ||
447 | |||
448 | # FIXME: Cross package confuse this check, so just skip them | ||
449 | for s in ['cross', 'nativesdk', 'cross-canadian']: | ||
450 | if bb.data.inherits_class(s, d): | ||
451 | return | ||
452 | |||
453 | # avoid following links to /usr/bin (e.g. on udev builds) | ||
454 | # we will check the files pointed to anyway... | ||
455 | if os.path.islink(path): | ||
456 | return | ||
457 | |||
458 | #if this will throw an exception, then fix the dict above | ||
459 | (machine, osabi, abiversion, littleendian, bits) \ | ||
460 | = package_qa_get_machine_dict()[target_os][target_arch] | ||
461 | |||
462 | # Check the architecture and endiannes of the binary | ||
463 | if not ((machine == elf.machine()) or \ | ||
464 | ((("virtual/kernel" in provides) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))): | ||
465 | messages["arch"] = "Architecture did not match (%d to %d) on %s" % \ | ||
466 | (machine, elf.machine(), package_qa_clean_path(path,d)) | ||
467 | elif not ((bits == elf.abiSize()) or \ | ||
468 | ((("virtual/kernel" in provides) or bb.data.inherits_class("module", d) ) and (target_os == "linux-gnux32" or target_os == "linux-gnun32"))): | ||
469 | messages["arch"] = "Bit size did not match (%d to %d) %s on %s" % \ | ||
470 | (bits, elf.abiSize(), bpn, package_qa_clean_path(path,d)) | ||
471 | elif not littleendian == elf.isLittleEndian(): | ||
472 | messages["arch"] = "Endiannes did not match (%d to %d) on %s" % \ | ||
473 | (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)) | ||
474 | |||
475 | QAPATHTEST[desktop] = "package_qa_check_desktop" | ||
476 | def package_qa_check_desktop(path, name, d, elf, messages): | ||
477 | """ | ||
478 | Run all desktop files through desktop-file-validate. | ||
479 | """ | ||
480 | if path.endswith(".desktop"): | ||
481 | desktop_file_validate = os.path.join(d.getVar('STAGING_BINDIR_NATIVE',True),'desktop-file-validate') | ||
482 | output = os.popen("%s %s" % (desktop_file_validate, path)) | ||
483 | # This only produces output on errors | ||
484 | for l in output: | ||
485 | messages["desktop"] = "Desktop file issue: " + l.strip() | ||
486 | |||
487 | QAPATHTEST[textrel] = "package_qa_textrel" | ||
488 | def package_qa_textrel(path, name, d, elf, messages): | ||
489 | """ | ||
490 | Check if the binary contains relocations in .text | ||
491 | """ | ||
492 | |||
493 | if not elf: | ||
494 | return | ||
495 | |||
496 | if os.path.islink(path): | ||
497 | return | ||
498 | |||
499 | phdrs = elf.run_objdump("-p", d) | ||
500 | sane = True | ||
501 | |||
502 | import re | ||
503 | textrel_re = re.compile("\s+TEXTREL\s+") | ||
504 | for line in phdrs.split("\n"): | ||
505 | if textrel_re.match(line): | ||
506 | sane = False | ||
507 | |||
508 | if not sane: | ||
509 | messages["textrel"] = "ELF binary '%s' has relocations in .text" % path | ||
510 | |||
511 | QAPATHTEST[ldflags] = "package_qa_hash_style" | ||
512 | def package_qa_hash_style(path, name, d, elf, messages): | ||
513 | """ | ||
514 | Check if the binary has the right hash style... | ||
515 | """ | ||
516 | |||
517 | if not elf: | ||
518 | return | ||
519 | |||
520 | if os.path.islink(path): | ||
521 | return | ||
522 | |||
523 | gnu_hash = "--hash-style=gnu" in d.getVar('LDFLAGS', True) | ||
524 | if not gnu_hash: | ||
525 | gnu_hash = "--hash-style=both" in d.getVar('LDFLAGS', True) | ||
526 | if not gnu_hash: | ||
527 | return | ||
528 | |||
529 | sane = False | ||
530 | has_syms = False | ||
531 | |||
532 | phdrs = elf.run_objdump("-p", d) | ||
533 | |||
534 | # If this binary has symbols, we expect it to have GNU_HASH too. | ||
535 | for line in phdrs.split("\n"): | ||
536 | if "SYMTAB" in line: | ||
537 | has_syms = True | ||
538 | if "GNU_HASH" in line: | ||
539 | sane = True | ||
540 | if "[mips32]" in line or "[mips64]" in line: | ||
541 | sane = True | ||
542 | |||
543 | if has_syms and not sane: | ||
544 | messages["ldflags"] = "No GNU_HASH in the elf binary: '%s'" % path | ||
545 | |||
546 | |||
547 | QAPATHTEST[buildpaths] = "package_qa_check_buildpaths" | ||
548 | def package_qa_check_buildpaths(path, name, d, elf, messages): | ||
549 | """ | ||
550 | Check for build paths inside target files and error if not found in the whitelist | ||
551 | """ | ||
552 | # Ignore .debug files, not interesting | ||
553 | if path.find(".debug") != -1: | ||
554 | return | ||
555 | |||
556 | # Ignore symlinks | ||
557 | if os.path.islink(path): | ||
558 | return | ||
559 | |||
560 | tmpdir = d.getVar('TMPDIR', True) | ||
561 | with open(path) as f: | ||
562 | file_content = f.read() | ||
563 | if tmpdir in file_content: | ||
564 | messages["buildpaths"] = "File %s in package contained reference to tmpdir" % package_qa_clean_path(path,d) | ||
565 | |||
566 | |||
567 | QAPATHTEST[xorg-driver-abi] = "package_qa_check_xorg_driver_abi" | ||
568 | def package_qa_check_xorg_driver_abi(path, name, d, elf, messages): | ||
569 | """ | ||
570 | Check that all packages containing Xorg drivers have ABI dependencies | ||
571 | """ | ||
572 | |||
573 | # Skip dev, dbg or nativesdk packages | ||
574 | if name.endswith("-dev") or name.endswith("-dbg") or name.startswith("nativesdk-"): | ||
575 | return | ||
576 | |||
577 | driverdir = d.expand("${libdir}/xorg/modules/drivers/") | ||
578 | if driverdir in path and path.endswith(".so"): | ||
579 | mlprefix = d.getVar('MLPREFIX', True) or '' | ||
580 | for rdep in bb.utils.explode_deps(d.getVar('RDEPENDS_' + name, True) or ""): | ||
581 | if rdep.startswith("%sxorg-abi-" % mlprefix): | ||
582 | return | ||
583 | messages["xorg-driver-abi"] = "Package %s contains Xorg driver (%s) but no xorg-abi- dependencies" % (name, os.path.basename(path)) | ||
584 | |||
585 | QAPATHTEST[infodir] = "package_qa_check_infodir" | ||
586 | def package_qa_check_infodir(path, name, d, elf, messages): | ||
587 | """ | ||
588 | Check that /usr/share/info/dir isn't shipped in a particular package | ||
589 | """ | ||
590 | infodir = d.expand("${infodir}/dir") | ||
591 | |||
592 | if infodir in path: | ||
593 | messages["infodir"] = "The /usr/share/info/dir file is not meant to be shipped in a particular package." | ||
594 | |||
595 | QAPATHTEST[symlink-to-sysroot] = "package_qa_check_symlink_to_sysroot" | ||
596 | def package_qa_check_symlink_to_sysroot(path, name, d, elf, messages): | ||
597 | """ | ||
598 | Check that the package doesn't contain any absolute symlinks to the sysroot. | ||
599 | """ | ||
600 | if os.path.islink(path): | ||
601 | target = os.readlink(path) | ||
602 | if os.path.isabs(target): | ||
603 | tmpdir = d.getVar('TMPDIR', True) | ||
604 | if target.startswith(tmpdir): | ||
605 | trimmed = path.replace(os.path.join (d.getVar("PKGDEST", True), name), "") | ||
606 | messages["symlink-to-sysroot"] = "Symlink %s in %s points to TMPDIR" % (trimmed, name) | ||
607 | |||
608 | def package_qa_check_license(workdir, d): | ||
609 | """ | ||
610 | Check for changes in the license files | ||
611 | """ | ||
612 | import tempfile | ||
613 | sane = True | ||
614 | |||
615 | lic_files = d.getVar('LIC_FILES_CHKSUM', True) | ||
616 | lic = d.getVar('LICENSE', True) | ||
617 | pn = d.getVar('PN', True) | ||
618 | |||
619 | if lic == "CLOSED": | ||
620 | return True | ||
621 | |||
622 | if not lic_files: | ||
623 | bb.error(pn + ": Recipe file does not have license file information (LIC_FILES_CHKSUM)") | ||
624 | return False | ||
625 | |||
626 | srcdir = d.getVar('S', True) | ||
627 | |||
628 | for url in lic_files.split(): | ||
629 | try: | ||
630 | (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(url) | ||
631 | except bb.fetch.MalformedUrl: | ||
632 | raise bb.build.FuncFailed( pn + ": LIC_FILES_CHKSUM contains an invalid URL: " + url) | ||
633 | srclicfile = os.path.join(srcdir, path) | ||
634 | if not os.path.isfile(srclicfile): | ||
635 | raise bb.build.FuncFailed( pn + ": LIC_FILES_CHKSUM points to an invalid file: " + srclicfile) | ||
636 | |||
637 | recipemd5 = parm.get('md5', '') | ||
638 | beginline, endline = 0, 0 | ||
639 | if 'beginline' in parm: | ||
640 | beginline = int(parm['beginline']) | ||
641 | if 'endline' in parm: | ||
642 | endline = int(parm['endline']) | ||
643 | |||
644 | if (not beginline) and (not endline): | ||
645 | md5chksum = bb.utils.md5_file(srclicfile) | ||
646 | else: | ||
647 | fi = open(srclicfile, 'rb') | ||
648 | fo = tempfile.NamedTemporaryFile(mode='wb', prefix='poky.', suffix='.tmp', delete=False) | ||
649 | tmplicfile = fo.name; | ||
650 | lineno = 0 | ||
651 | linesout = 0 | ||
652 | for line in fi: | ||
653 | lineno += 1 | ||
654 | if (lineno >= beginline): | ||
655 | if ((lineno <= endline) or not endline): | ||
656 | fo.write(line) | ||
657 | linesout += 1 | ||
658 | else: | ||
659 | break | ||
660 | fo.flush() | ||
661 | fo.close() | ||
662 | fi.close() | ||
663 | md5chksum = bb.utils.md5_file(tmplicfile) | ||
664 | os.unlink(tmplicfile) | ||
665 | |||
666 | if recipemd5 == md5chksum: | ||
667 | bb.note (pn + ": md5 checksum matched for ", url) | ||
668 | else: | ||
669 | if recipemd5: | ||
670 | bb.error(pn + ": md5 data is not matching for ", url) | ||
671 | bb.error(pn + ": The new md5 checksum is ", md5chksum) | ||
672 | if beginline: | ||
673 | if endline: | ||
674 | srcfiledesc = "%s (lines %d through to %d)" % (srclicfile, beginline, endline) | ||
675 | else: | ||
676 | srcfiledesc = "%s (beginning on line %d)" % (srclicfile, beginline) | ||
677 | elif endline: | ||
678 | srcfiledesc = "%s (ending on line %d)" % (srclicfile, endline) | ||
679 | else: | ||
680 | srcfiledesc = srclicfile | ||
681 | bb.error(pn + ": Check if the license information has changed in %s to verify that the LICENSE value \"%s\" remains valid" % (srcfiledesc, lic)) | ||
682 | else: | ||
683 | bb.error(pn + ": md5 checksum is not specified for ", url) | ||
684 | bb.error(pn + ": The md5 checksum is ", md5chksum) | ||
685 | sane = False | ||
686 | |||
687 | return sane | ||
688 | |||
689 | def package_qa_check_staged(path,d): | ||
690 | """ | ||
691 | Check staged la and pc files for sanity | ||
692 | -e.g. installed being false | ||
693 | |||
694 | As this is run after every stage we should be able | ||
695 | to find the one responsible for the errors easily even | ||
696 | if we look at every .pc and .la file | ||
697 | """ | ||
698 | |||
699 | sane = True | ||
700 | tmpdir = d.getVar('TMPDIR', True) | ||
701 | workdir = os.path.join(tmpdir, "work") | ||
702 | |||
703 | installed = "installed=yes" | ||
704 | if bb.data.inherits_class("native", d) or bb.data.inherits_class("cross", d): | ||
705 | pkgconfigcheck = workdir | ||
706 | else: | ||
707 | pkgconfigcheck = tmpdir | ||
708 | |||
709 | # find all .la and .pc files | ||
710 | # read the content | ||
711 | # and check for stuff that looks wrong | ||
712 | for root, dirs, files in os.walk(path): | ||
713 | for file in files: | ||
714 | path = os.path.join(root,file) | ||
715 | if file.endswith(".la"): | ||
716 | with open(path) as f: | ||
717 | file_content = f.read() | ||
718 | if workdir in file_content: | ||
719 | error_msg = "%s failed sanity test (workdir) in path %s" % (file,root) | ||
720 | sane = package_qa_handle_error("la", error_msg, d) | ||
721 | elif file.endswith(".pc"): | ||
722 | with open(path) as f: | ||
723 | file_content = f.read() | ||
724 | if pkgconfigcheck in file_content: | ||
725 | error_msg = "%s failed sanity test (tmpdir) in path %s" % (file,root) | ||
726 | sane = package_qa_handle_error("pkgconfig", error_msg, d) | ||
727 | |||
728 | return sane | ||
729 | |||
730 | # Walk over all files in a directory and call func | ||
731 | def package_qa_walk(path, warnfuncs, errorfuncs, skip, package, d): | ||
732 | import oe.qa | ||
733 | |||
734 | #if this will throw an exception, then fix the dict above | ||
735 | target_os = d.getVar('TARGET_OS', True) | ||
736 | target_arch = d.getVar('TARGET_ARCH', True) | ||
737 | |||
738 | warnings = {} | ||
739 | errors = {} | ||
740 | for path in pkgfiles[package]: | ||
741 | elf = oe.qa.ELFFile(path) | ||
742 | try: | ||
743 | elf.open() | ||
744 | except: | ||
745 | elf = None | ||
746 | for func in warnfuncs: | ||
747 | func(path, package, d, elf, warnings) | ||
748 | for func in errorfuncs: | ||
749 | func(path, package, d, elf, errors) | ||
750 | |||
751 | for w in warnings: | ||
752 | package_qa_handle_error(w, warnings[w], d) | ||
753 | for e in errors: | ||
754 | package_qa_handle_error(e, errors[e], d) | ||
755 | |||
756 | return len(errors) == 0 | ||
757 | |||
758 | def package_qa_check_rdepends(pkg, pkgdest, skip, taskdeps, packages, d): | ||
759 | # Don't do this check for kernel/module recipes, there aren't too many debug/development | ||
760 | # packages and you can get false positives e.g. on kernel-module-lirc-dev | ||
761 | if bb.data.inherits_class("kernel", d) or bb.data.inherits_class("module-base", d): | ||
762 | return True | ||
763 | |||
764 | sane = True | ||
765 | if not "-dbg" in pkg and not "packagegroup-" in pkg and not "-image" in pkg: | ||
766 | localdata = bb.data.createCopy(d) | ||
767 | localdata.setVar('OVERRIDES', pkg) | ||
768 | bb.data.update_data(localdata) | ||
769 | |||
770 | # Now check the RDEPENDS | ||
771 | rdepends = bb.utils.explode_deps(localdata.getVar('RDEPENDS', True) or "") | ||
772 | |||
773 | # Now do the sanity check!!! | ||
774 | for rdepend in rdepends: | ||
775 | if "-dbg" in rdepend and "debug-deps" not in skip: | ||
776 | error_msg = "%s rdepends on %s" % (pkg,rdepend) | ||
777 | sane = package_qa_handle_error("debug-deps", error_msg, d) | ||
778 | if (not "-dev" in pkg and not "-staticdev" in pkg) and rdepend.endswith("-dev") and "dev-deps" not in skip: | ||
779 | error_msg = "%s rdepends on %s" % (pkg, rdepend) | ||
780 | sane = package_qa_handle_error("dev-deps", error_msg, d) | ||
781 | if rdepend not in packages: | ||
782 | rdep_data = oe.packagedata.read_subpkgdata(rdepend, d) | ||
783 | if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps: | ||
784 | continue | ||
785 | if not rdep_data or not 'PN' in rdep_data: | ||
786 | pkgdata_dir = d.getVar("PKGDATA_DIR", True) | ||
787 | try: | ||
788 | possibles = os.listdir("%s/runtime-rprovides/%s/" % (pkgdata_dir, rdepend)) | ||
789 | except OSError: | ||
790 | possibles = [] | ||
791 | for p in possibles: | ||
792 | rdep_data = oe.packagedata.read_subpkgdata(p, d) | ||
793 | if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps: | ||
794 | break | ||
795 | if rdep_data and 'PN' in rdep_data and rdep_data['PN'] in taskdeps: | ||
796 | continue | ||
797 | error_msg = "%s rdepends on %s, but it isn't a build dependency?" % (pkg, rdepend) | ||
798 | sane = package_qa_handle_error("build-deps", error_msg, d) | ||
799 | |||
800 | if "file-rdeps" not in skip: | ||
801 | ignored_file_rdeps = set(['/bin/sh', '/usr/bin/env', 'rtld(GNU_HASH)']) | ||
802 | if bb.data.inherits_class('nativesdk', d): | ||
803 | ignored_file_rdeps |= set(['/bin/bash', '/usr/bin/perl']) | ||
804 | # For Saving the FILERDEPENDS | ||
805 | filerdepends = set() | ||
806 | rdep_data = oe.packagedata.read_subpkgdata(pkg, d) | ||
807 | for key in rdep_data: | ||
808 | if key.startswith("FILERDEPENDS_"): | ||
809 | for subkey in rdep_data[key].split(): | ||
810 | filerdepends.add(subkey) | ||
811 | filerdepends -= ignored_file_rdeps | ||
812 | |||
813 | if filerdepends: | ||
814 | next = rdepends | ||
815 | done = rdepends[:] | ||
816 | # Find all the rdepends on the dependency chain | ||
817 | while next: | ||
818 | new = [] | ||
819 | for rdep in next: | ||
820 | rdep_data = oe.packagedata.read_subpkgdata(rdep, d) | ||
821 | sub_rdeps = rdep_data.get("RDEPENDS_" + rdep) | ||
822 | if not sub_rdeps: | ||
823 | continue | ||
824 | for sub_rdep in sub_rdeps.split(): | ||
825 | if sub_rdep in done: | ||
826 | continue | ||
827 | if not sub_rdep.startswith('(') and \ | ||
828 | oe.packagedata.has_subpkgdata(sub_rdep, d): | ||
829 | # It's a new rdep | ||
830 | done.append(sub_rdep) | ||
831 | new.append(sub_rdep) | ||
832 | next = new | ||
833 | |||
834 | # Add the rprovides of itself | ||
835 | if pkg not in done: | ||
836 | done.insert(0, pkg) | ||
837 | |||
838 | # The python is not a package, but python-core provides it, so | ||
839 | # skip checking /usr/bin/python if python is in the rdeps, in | ||
840 | # case there is a RDEPENDS_pkg = "python" in the recipe. | ||
841 | for py in [ d.getVar('MLPREFIX', True) + "python", "python" ]: | ||
842 | if py in done: | ||
843 | filerdepends.discard("/usr/bin/python") | ||
844 | done.remove(py) | ||
845 | for rdep in done: | ||
846 | # For Saving the FILERPROVIDES, RPROVIDES and FILES_INFO | ||
847 | rdep_rprovides = set() | ||
848 | rdep_data = oe.packagedata.read_subpkgdata(rdep, d) | ||
849 | for key in rdep_data: | ||
850 | if key.startswith("FILERPROVIDES_") or key.startswith("RPROVIDES_"): | ||
851 | for subkey in rdep_data[key].split(): | ||
852 | rdep_rprovides.add(subkey) | ||
853 | # Add the files list to the rprovides | ||
854 | if key == "FILES_INFO": | ||
855 | # Use eval() to make it as a dict | ||
856 | for subkey in eval(rdep_data[key]): | ||
857 | rdep_rprovides.add(subkey) | ||
858 | filerdepends -= rdep_rprovides | ||
859 | if not filerdepends: | ||
860 | # Break if all the file rdepends are met | ||
861 | break | ||
862 | else: | ||
863 | # Clear it for the next loop | ||
864 | rdep_rprovides.clear() | ||
865 | if filerdepends: | ||
866 | error_msg = "%s requires %s, but no providers in its RDEPENDS" % \ | ||
867 | (pkg, ', '.join(str(e) for e in filerdepends)) | ||
868 | sane = package_qa_handle_error("file-rdeps", error_msg, d) | ||
869 | |||
870 | return sane | ||
871 | |||
872 | def package_qa_check_deps(pkg, pkgdest, skip, d): | ||
873 | sane = True | ||
874 | |||
875 | localdata = bb.data.createCopy(d) | ||
876 | localdata.setVar('OVERRIDES', pkg) | ||
877 | bb.data.update_data(localdata) | ||
878 | |||
879 | def check_valid_deps(var): | ||
880 | sane = True | ||
881 | try: | ||
882 | rvar = bb.utils.explode_dep_versions2(localdata.getVar(var, True) or "") | ||
883 | except ValueError as e: | ||
884 | bb.fatal("%s_%s: %s" % (var, pkg, e)) | ||
885 | for dep in rvar: | ||
886 | for v in rvar[dep]: | ||
887 | if v and not v.startswith(('< ', '= ', '> ', '<= ', '>=')): | ||
888 | error_msg = "%s_%s is invalid: %s (%s) only comparisons <, =, >, <=, and >= are allowed" % (var, pkg, dep, v) | ||
889 | sane = package_qa_handle_error("dep-cmp", error_msg, d) | ||
890 | return sane | ||
891 | |||
892 | sane = True | ||
893 | if not check_valid_deps('RDEPENDS'): | ||
894 | sane = False | ||
895 | if not check_valid_deps('RRECOMMENDS'): | ||
896 | sane = False | ||
897 | if not check_valid_deps('RSUGGESTS'): | ||
898 | sane = False | ||
899 | if not check_valid_deps('RPROVIDES'): | ||
900 | sane = False | ||
901 | if not check_valid_deps('RREPLACES'): | ||
902 | sane = False | ||
903 | if not check_valid_deps('RCONFLICTS'): | ||
904 | sane = False | ||
905 | |||
906 | return sane | ||
907 | |||
908 | # The PACKAGE FUNC to scan each package | ||
909 | python do_package_qa () { | ||
910 | import subprocess | ||
911 | import oe.packagedata | ||
912 | |||
913 | bb.note("DO PACKAGE QA") | ||
914 | |||
915 | bb.build.exec_func("read_subpackage_metadata", d) | ||
916 | |||
917 | logdir = d.getVar('T', True) | ||
918 | pkg = d.getVar('PN', True) | ||
919 | |||
920 | # Check the compile log for host contamination | ||
921 | compilelog = os.path.join(logdir,"log.do_compile") | ||
922 | |||
923 | if os.path.exists(compilelog): | ||
924 | statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % compilelog | ||
925 | if subprocess.call(statement, shell=True) == 0: | ||
926 | msg = "%s: The compile log indicates that host include and/or library paths were used.\n \ | ||
927 | Please check the log '%s' for more information." % (pkg, compilelog) | ||
928 | package_qa_handle_error("compile-host-path", msg, d) | ||
929 | |||
930 | # Check the install log for host contamination | ||
931 | installlog = os.path.join(logdir,"log.do_install") | ||
932 | |||
933 | if os.path.exists(installlog): | ||
934 | statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % installlog | ||
935 | if subprocess.call(statement, shell=True) == 0: | ||
936 | msg = "%s: The install log indicates that host include and/or library paths were used.\n \ | ||
937 | Please check the log '%s' for more information." % (pkg, installlog) | ||
938 | package_qa_handle_error("install-host-path", msg, d) | ||
939 | |||
940 | # Scan the packages... | ||
941 | pkgdest = d.getVar('PKGDEST', True) | ||
942 | packages = d.getVar('PACKAGES', True) | ||
943 | |||
944 | cpath = oe.cachedpath.CachedPath() | ||
945 | global pkgfiles | ||
946 | pkgfiles = {} | ||
947 | for pkg in (packages or "").split(): | ||
948 | pkgfiles[pkg] = [] | ||
949 | for walkroot, dirs, files in cpath.walk(pkgdest + "/" + pkg): | ||
950 | for file in files: | ||
951 | pkgfiles[pkg].append(walkroot + os.sep + file) | ||
952 | |||
953 | # no packages should be scanned | ||
954 | if not packages: | ||
955 | return | ||
956 | |||
957 | testmatrix = d.getVarFlags("QAPATHTEST") | ||
958 | import re | ||
959 | # The package name matches the [a-z0-9.+-]+ regular expression | ||
960 | pkgname_pattern = re.compile("^[a-z0-9.+-]+$") | ||
961 | |||
962 | taskdepdata = d.getVar("BB_TASKDEPDATA", False) | ||
963 | taskdeps = set() | ||
964 | for dep in taskdepdata: | ||
965 | taskdeps.add(taskdepdata[dep][0]) | ||
966 | |||
967 | g = globals() | ||
968 | walk_sane = True | ||
969 | rdepends_sane = True | ||
970 | deps_sane = True | ||
971 | for package in packages.split(): | ||
972 | skip = (d.getVar('INSANE_SKIP_' + package, True) or "").split() | ||
973 | if skip: | ||
974 | bb.note("Package %s skipping QA tests: %s" % (package, str(skip))) | ||
975 | warnchecks = [] | ||
976 | for w in (d.getVar("WARN_QA", True) or "").split(): | ||
977 | if w in skip: | ||
978 | continue | ||
979 | if w in testmatrix and testmatrix[w] in g: | ||
980 | warnchecks.append(g[testmatrix[w]]) | ||
981 | errorchecks = [] | ||
982 | for e in (d.getVar("ERROR_QA", True) or "").split(): | ||
983 | if e in skip: | ||
984 | continue | ||
985 | if e in testmatrix and testmatrix[e] in g: | ||
986 | errorchecks.append(g[testmatrix[e]]) | ||
987 | |||
988 | bb.note("Checking Package: %s" % package) | ||
989 | # Check package name | ||
990 | if not pkgname_pattern.match(package): | ||
991 | package_qa_handle_error("pkgname", | ||
992 | "%s doesn't match the [a-z0-9.+-]+ regex\n" % package, d) | ||
993 | |||
994 | path = "%s/%s" % (pkgdest, package) | ||
995 | if not package_qa_walk(path, warnchecks, errorchecks, skip, package, d): | ||
996 | walk_sane = False | ||
997 | if not package_qa_check_rdepends(package, pkgdest, skip, taskdeps, packages, d): | ||
998 | rdepends_sane = False | ||
999 | if not package_qa_check_deps(package, pkgdest, skip, d): | ||
1000 | deps_sane = False | ||
1001 | |||
1002 | |||
1003 | if 'libdir' in d.getVar("ALL_QA", True).split(): | ||
1004 | package_qa_check_libdir(d) | ||
1005 | |||
1006 | qa_sane = d.getVar("QA_SANE", True) | ||
1007 | if not walk_sane or not rdepends_sane or not deps_sane or not qa_sane: | ||
1008 | bb.fatal("QA run found fatal errors. Please consider fixing them.") | ||
1009 | bb.note("DONE with PACKAGE QA") | ||
1010 | } | ||
1011 | |||
1012 | do_package_qa[rdeptask] = "do_packagedata" | ||
1013 | addtask do_package_qa after do_packagedata do_package before do_build | ||
1014 | |||
1015 | SSTATETASKS += "do_package_qa" | ||
1016 | do_package_qa[sstate-inputdirs] = "" | ||
1017 | do_package_qa[sstate-outputdirs] = "" | ||
1018 | python do_package_qa_setscene () { | ||
1019 | sstate_setscene(d) | ||
1020 | } | ||
1021 | addtask do_package_qa_setscene | ||
1022 | |||
1023 | python do_qa_staging() { | ||
1024 | bb.note("QA checking staging") | ||
1025 | |||
1026 | if not package_qa_check_staged(d.expand('${SYSROOT_DESTDIR}${STAGING_LIBDIR}'), d): | ||
1027 | bb.fatal("QA staging was broken by the package built above") | ||
1028 | } | ||
1029 | |||
1030 | python do_qa_configure() { | ||
1031 | import subprocess | ||
1032 | |||
1033 | ########################################################################### | ||
1034 | # Check config.log for cross compile issues | ||
1035 | ########################################################################### | ||
1036 | |||
1037 | configs = [] | ||
1038 | workdir = d.getVar('WORKDIR', True) | ||
1039 | bb.note("Checking autotools environment for common misconfiguration") | ||
1040 | for root, dirs, files in os.walk(workdir): | ||
1041 | statement = "grep -e 'CROSS COMPILE Badness:' -e 'is unsafe for cross-compilation' %s > /dev/null" % \ | ||
1042 | os.path.join(root,"config.log") | ||
1043 | if "config.log" in files: | ||
1044 | if subprocess.call(statement, shell=True) == 0: | ||
1045 | bb.fatal("""This autoconf log indicates errors, it looked at host include and/or library paths while determining system capabilities. | ||
1046 | Rerun configure task after fixing this. The path was '%s'""" % root) | ||
1047 | |||
1048 | if "configure.ac" in files: | ||
1049 | configs.append(os.path.join(root,"configure.ac")) | ||
1050 | if "configure.in" in files: | ||
1051 | configs.append(os.path.join(root, "configure.in")) | ||
1052 | |||
1053 | ########################################################################### | ||
1054 | # Check gettext configuration and dependencies are correct | ||
1055 | ########################################################################### | ||
1056 | |||
1057 | cnf = d.getVar('EXTRA_OECONF', True) or "" | ||
1058 | if "gettext" not in d.getVar('P', True) and "gcc-runtime" not in d.getVar('P', True) and "--disable-nls" not in cnf: | ||
1059 | ml = d.getVar("MLPREFIX", True) or "" | ||
1060 | if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d) or bb.data.inherits_class('nativesdk', d): | ||
1061 | gt = "gettext-native" | ||
1062 | elif bb.data.inherits_class('cross-canadian', d): | ||
1063 | gt = "nativesdk-gettext" | ||
1064 | else: | ||
1065 | gt = "virtual/" + ml + "gettext" | ||
1066 | deps = bb.utils.explode_deps(d.getVar('DEPENDS', True) or "") | ||
1067 | if gt not in deps: | ||
1068 | for config in configs: | ||
1069 | gnu = "grep \"^[[:space:]]*AM_GNU_GETTEXT\" %s >/dev/null" % config | ||
1070 | if subprocess.call(gnu, shell=True) == 0: | ||
1071 | bb.fatal("""%s required but not in DEPENDS for file %s. | ||
1072 | Missing inherit gettext?""" % (gt, config)) | ||
1073 | |||
1074 | ########################################################################### | ||
1075 | # Check license variables | ||
1076 | ########################################################################### | ||
1077 | |||
1078 | if not package_qa_check_license(workdir, d): | ||
1079 | bb.fatal("Licensing Error: LIC_FILES_CHKSUM does not match, please fix") | ||
1080 | |||
1081 | ########################################################################### | ||
1082 | # Check unrecognised configure options (with a white list) | ||
1083 | ########################################################################### | ||
1084 | if bb.data.inherits_class("autotools", d): | ||
1085 | bb.note("Checking configure output for unrecognised options") | ||
1086 | try: | ||
1087 | flag = "WARNING: unrecognized options:" | ||
1088 | log = os.path.join(d.getVar('B', True), 'config.log') | ||
1089 | output = subprocess.check_output(['grep', '-F', flag, log]).replace(', ', ' ') | ||
1090 | options = set() | ||
1091 | for line in output.splitlines(): | ||
1092 | options |= set(line.partition(flag)[2].split()) | ||
1093 | whitelist = set(d.getVar("UNKNOWN_CONFIGURE_WHITELIST", True).split()) | ||
1094 | options -= whitelist | ||
1095 | if options: | ||
1096 | pn = d.getVar('PN', True) | ||
1097 | error_msg = pn + ": configure was passed unrecognised options: " + " ".join(options) | ||
1098 | package_qa_handle_error("unknown-configure-option", error_msg, d) | ||
1099 | except subprocess.CalledProcessError: | ||
1100 | pass | ||
1101 | } | ||
1102 | # The Staging Func, to check all staging | ||
1103 | #addtask qa_staging after do_populate_sysroot before do_build | ||
1104 | do_populate_sysroot[postfuncs] += "do_qa_staging " | ||
1105 | |||
1106 | # Check broken config.log files, for packages requiring Gettext which don't | ||
1107 | # have it in DEPENDS and for correct LIC_FILES_CHKSUM | ||
1108 | #addtask qa_configure after do_configure before do_compile | ||
1109 | do_configure[postfuncs] += "do_qa_configure " | ||
1110 | |||
1111 | python () { | ||
1112 | tests = d.getVar('ALL_QA', True).split() | ||
1113 | if "desktop" in tests: | ||
1114 | d.appendVar("PACKAGE_DEPENDS", "desktop-file-utils-native") | ||
1115 | |||
1116 | ########################################################################### | ||
1117 | # Check various variables | ||
1118 | ########################################################################### | ||
1119 | |||
1120 | # Checking ${FILESEXTRAPATHS} | ||
1121 | extrapaths = (d.getVar("FILESEXTRAPATHS", True) or "") | ||
1122 | if '__default' not in extrapaths.split(":"): | ||
1123 | msg = "FILESEXTRAPATHS-variable, must always use _prepend (or _append)\n" | ||
1124 | msg += "type of assignment, and don't forget the colon.\n" | ||
1125 | msg += "Please assign it with the format of:\n" | ||
1126 | msg += " FILESEXTRAPATHS_append := \":${THISDIR}/Your_Files_Path\" or\n" | ||
1127 | msg += " FILESEXTRAPATHS_prepend := \"${THISDIR}/Your_Files_Path:\"\n" | ||
1128 | msg += "in your bbappend file\n\n" | ||
1129 | msg += "Your incorrect assignment is:\n" | ||
1130 | msg += "%s\n" % extrapaths | ||
1131 | bb.warn(msg) | ||
1132 | |||
1133 | if d.getVar('do_stage', True) is not None: | ||
1134 | bb.fatal("Legacy staging found for %s as it has a do_stage function. This will need conversion to a do_install or often simply removal to work with OE-core" % d.getVar("FILE", True)) | ||
1135 | |||
1136 | overrides = d.getVar('OVERRIDES', True).split(':') | ||
1137 | pn = d.getVar('PN', True) | ||
1138 | if pn in overrides: | ||
1139 | msg = 'Recipe %s has PN of "%s" which is in OVERRIDES, this can result in unexpected behaviour.' % (d.getVar("FILE", True), pn) | ||
1140 | package_qa_handle_error("pn-overrides", msg, d) | ||
1141 | |||
1142 | issues = [] | ||
1143 | if (d.getVar('PACKAGES', True) or "").split(): | ||
1144 | for dep in (d.getVar('QADEPENDS', True) or "").split(): | ||
1145 | d.appendVarFlag('do_package_qa', 'depends', " %s:do_populate_sysroot" % dep) | ||
1146 | for var in 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RCONFLICTS', 'RPROVIDES', 'RREPLACES', 'FILES', 'pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm', 'ALLOW_EMPTY': | ||
1147 | if d.getVar(var): | ||
1148 | issues.append(var) | ||
1149 | else: | ||
1150 | d.setVarFlag('do_package_qa', 'rdeptask', '') | ||
1151 | for i in issues: | ||
1152 | package_qa_handle_error("pkgvarcheck", "%s: Variable %s is set as not being package specific, please fix this." % (d.getVar("FILE", True), i), d) | ||
1153 | } | ||