summaryrefslogtreecommitdiffstats
path: root/meta/classes/insane.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'meta/classes/insane.bbclass')
-rw-r--r--meta/classes/insane.bbclass344
1 files changed, 322 insertions, 22 deletions
diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
index 4bceaaed82..cf74810f3e 100644
--- a/meta/classes/insane.bbclass
+++ b/meta/classes/insane.bbclass
@@ -9,6 +9,7 @@
9# -Check if .la files wrongly point to workdir 9# -Check if .la files wrongly point to workdir
10# -Check if .pc files wrongly point to workdir 10# -Check if .pc files wrongly point to workdir
11# -Check if packages contains .debug directories or .so files where they should be in -dev or -dbg 11# -Check if packages contains .debug directories or .so files where they should be in -dev or -dbg
12# -Check if config.log contains traces to broken autoconf tests
12# 13#
13 14
14 15
@@ -21,26 +22,219 @@
21# We play a special package function 22# We play a special package function
22inherit package 23inherit package
23PACKAGE_DEPENDS += "pax-utils-native" 24PACKAGE_DEPENDS += "pax-utils-native"
25#PACKAGE_DEPENDS += chrpath-native"
24PACKAGEFUNCS += " do_package_qa " 26PACKAGEFUNCS += " do_package_qa "
25 27
28
29#
30# dictionary for elf headers
31#
32# feel free to add and correct.
33#
34# TARGET_OS TARGET_ARCH MACHINE, OSABI, ABIVERSION, Little Endian, 32bit?
35def package_qa_get_machine_dict():
36 return {
37 "linux" : {
38 "arm" : (40, 97, 0, True, True),
39 "armeb": (40, 97, 0, False, True),
40 "powerpc": (20, 0, 0, False, True),
41 "i386": ( 3, 0, 0, True, True),
42 "i486": ( 3, 0, 0, True, True),
43 "i586": ( 3, 0, 0, True, True),
44 "i686": ( 3, 0, 0, True, True),
45 "x64_64": (62, 0, 0, True, False),
46 "ia64": (50, 0, 0, True, False),
47 "alpha": (36902, 0, 0, True, False),
48 "hppa": (15, 3, 0, False, True),
49 "m68k": ( 4, 0, 0, False, True),
50 "mips": ( 8, 0, 0, False, True),
51 "mipsel": ( 8, 0, 0, True, True),
52 "s390": (22, 0, 0, False, True),
53 "sh4": (42, 0, 0, True, True),
54 "sparc": ( 2, 0, 0, False, True),
55 },
56 "linux-uclibc" : {
57 "arm" : ( 40, 97, 0, True, True),
58 "armeb": ( 40, 97, 0, False, True),
59 "powerpc": ( 20, 0, 0, False, True),
60 "mipsel": ( 8, 0, 0, True, True),
61 "avr32": (6317, 0, 0, False, True),
62 },
63 "uclinux-uclibc" : {
64 "bfin": ( 106, 0, 0, True, True),
65 },
66 "linux-gnueabi" : {
67 "arm" : (40, 0, 0, True, True),
68 "armeb" : (40, 0, 0, False, True),
69 },
70 "linux-uclibcgnueabi" : {
71 "arm" : (40, 0, 0, True, True),
72 "armeb" : (40, 0, 0, False, True),
73 },
74
75 }
76
77# factory for a class, embedded in a method
78def package_qa_get_elf(path, bits32):
79 class ELFFile:
80 EI_NIDENT = 16
81
82 EI_CLASS = 4
83 EI_DATA = 5
84 EI_VERSION = 6
85 EI_OSABI = 7
86 EI_ABIVERSION = 8
87
88 # possible values for EI_CLASS
89 ELFCLASSNONE = 0
90 ELFCLASS32 = 1
91 ELFCLASS64 = 2
92
93 # possible value for EI_VERSION
94 EV_CURRENT = 1
95
96 # possible values for EI_DATA
97 ELFDATANONE = 0
98 ELFDATA2LSB = 1
99 ELFDATA2MSB = 2
100
101 def my_assert(expectation, result):
102 if not expectation == result:
103 #print "'%x','%x'" % (ord(expectation), ord(result))
104 raise "This does not work as expected"
105 my_assert = staticmethod(my_assert)
106
107 def __init__(self, name):
108 self.name = name
109
110 def open(self):
111 self.file = file(self.name, "r")
112 self.data = self.file.read(ELFFile.EI_NIDENT+4)
113
114 ELFFile.my_assert(len(self.data), ELFFile.EI_NIDENT+4)
115 ELFFile.my_assert(self.data[0], chr(0x7f) )
116 ELFFile.my_assert(self.data[1], 'E')
117 ELFFile.my_assert(self.data[2], 'L')
118 ELFFile.my_assert(self.data[3], 'F')
119 if bits32 :
120 ELFFile.my_assert(self.data[ELFFile.EI_CLASS], chr(ELFFile.ELFCLASS32)) # only 32 bits
121 else:
122 ELFFile.my_assert(self.data[ELFFile.EI_CLASS], chr(ELFFile.ELFCLASS64)) # only 64 bits
123 ELFFile.my_assert(self.data[ELFFile.EI_VERSION], chr(ELFFile.EV_CURRENT) )
124
125 self.sex = self.data[ELFFile.EI_DATA]
126 if self.sex == chr(ELFFile.ELFDATANONE):
127 raise "Can't be"
128 elif self.sex == chr(ELFFile.ELFDATA2LSB):
129 self.sex = "<"
130 elif self.sex == chr(ELFFile.ELFDATA2MSB):
131 self.sex = ">"
132 else:
133 raise "Even more worse"
134
135 def osAbi(self):
136 return ord(self.data[ELFFile.EI_OSABI])
137
138 def abiVersion(self):
139 return ord(self.data[ELFFile.EI_ABIVERSION])
140
141 def isLittleEndian(self):
142 return self.sex == "<"
143
144 def isBigEngian(self):
145 return self.sex == ">"
146
147 def machine(self):
148 """
149 We know the sex stored in self.sex and we
150 know the position
151 """
152 import struct
153 (a,) = struct.unpack(self.sex+"H", self.data[18:20])
154 return a
155
156 return ELFFile(path)
157
158
159#
160#
161# Known Error classes
162# 0 - non dev contains .so
163# 1 - package contains a dangerous RPATH
164# 2 - package depends on debug package
165# 3 - non dbg contains .so
166# 4 - wrong architecture
167# 5 - .la contains installed=yes or reference to the workdir
168# 6 - .pc contains reference to /usr/include or workdir
169#
170#
171
172def package_qa_clean_path(path,d):
173 import bb
174 return path.replace(bb.data.getVar('TMPDIR',d,True),"")
175
176def package_qa_make_fatal_error(error_class, name, path,d):
177 """
178 decide if an error is fatal
179
180 TODO: Load a whitelist of known errors
181 """
182 if error_class == 0:
183 return False
184 else:
185 return True
186
187def package_qa_write_error(error_class, name, path, d):
188 import bb, os
189 if not bb.data.getVar('QA_LOG', d):
190 return
191
192 ERROR_NAMES =[
193 "non dev contains .so",
194 "package contains RPATH",
195 "package depends on debug package",
196 "non dbg contains .debug",
197 "wrong architecture",
198 "evil hides inside the .la",
199 "evil hides inside the .pc",
200 ]
201
202
203 log_path = os.path.join( bb.data.getVar('T', d, True), "log.qa_package" )
204 f = file( log_path, "a+")
205 print >> f, "%s, %s, %s" % (ERROR_NAMES[error_class], name, package_qa_clean_path(path,d))
206 f.close()
207
208
26def package_qa_check_rpath(file,name,d): 209def package_qa_check_rpath(file,name,d):
27 """ 210 """
28 Check for dangerous RPATHs 211 Check for dangerous RPATHs
29 """ 212 """
30 import bb, os 213 import bb, os
31 scanelf = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'scanelf') 214 scanelf = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'scanelf')
215 #chrpath = os.path.join(bb.data.getVar('STAGING_BINDIR_NATIVE',d,True),'chrpath')
32 bad_dir = bb.data.getVar('TMPDIR', d, True) + "/work" 216 bad_dir = bb.data.getVar('TMPDIR', d, True) + "/work"
217 bad_dir_test = bb.data.getVar('TMPDIR', d, True)
33 if not os.path.exists(scanelf): 218 if not os.path.exists(scanelf):
34 bb.note("Can not check RPATH scanelf not found") 219 bb.fatal("Can not check RPATH, scanelf (part of pax-utils-native) not found")
220 #if not os.path.exists(chrpath):
221 # bb.fatal("Can not fix RPATH, chrpath (part of chrpath-native) not found")
35 if not bad_dir in bb.data.getVar('WORKDIR', d, True): 222 if not bad_dir in bb.data.getVar('WORKDIR', d, True):
36 bb.fatal("This class assumed that WORKDIR is ${TMPDIR}/work... Not doing any check") 223 bb.fatal("This class assumed that WORKDIR is ${TMPDIR}/work... Not doing any check")
37 224
38 output = os.popen("%s -Byr %s" % (scanelf,file)) 225 #bb.note("%s -B -F%%r#F %s" % (scanelf,file))
39 txt = output.readline().rsplit() 226 output = os.popen("%s -B -F%%r#F '%s'" % (scanelf,file))
40 if bad_dir in txt: 227 txt = output.readline().split()
41 bb.fatal("QA Issue package %s contains bad RPATH %s in file %s" % (name, txt, file)) 228 #bb.note("???%s???" % bad_dir_test)
42 229 for line in txt:
43 pass 230 #bb.note("===%s===" % line)
231 if bad_dir in line:
232 package_qa_write_error( 1, name, file, d)
233 bb.error("QA Issue package %s contains bad RPATH %s in file %s" % (name, line, file))
234 #bb.note("Fixing RPATH for you in %s" % file)
235 #os.popen("%s -r /lib %s" % (chrpath,file))
236 #return False
237 return True
44 238
45def package_qa_check_devdbg(path, name,d): 239def package_qa_check_devdbg(path, name,d):
46 """ 240 """
@@ -49,50 +243,131 @@ def package_qa_check_devdbg(path, name,d):
49 """ 243 """
50 244
51 import bb, os 245 import bb, os
246 sane = True
247
52 if not "-dev" in name: 248 if not "-dev" in name:
53 if path[-3:] == ".so" and os.path.islink(path): 249 if path[-3:] == ".so" and os.path.islink(path):
54 bb.fatal("QA Issue: non -dev package %s contains symlink .so: %s" % (name, os.path.basename (path))) 250 package_qa_write_error( 0, name, path, d )
251 bb.error("QA Issue: non -dev package contains symlink .so: %s path '%s'" % (name, package_qa_clean_path(path,d)))
252 if package_qa_make_fatal_error( 0, name, path, d ):
253 sane = False
55 254
56 if not "-dbg" in name: 255 if not "-dbg" in name:
57 if '.debug' in path: 256 if '.debug' in path:
58 bb.fatal("QA Issue: non -dbg package contains .debug directory: %s" % name) 257 package_qa_write_error( 3, name, path, d )
258 bb.error("QA Issue: non debug package contains .debug directory: %s path %s" % (name, package_qa_clean_path(path,d)))
259 if package_qa_make_fatal_error( 3, name, path, d ):
260 sane = False
261
262 return sane
59 263
60def package_qa_check_perm(path,name,d): 264def package_qa_check_perm(path,name,d):
61 """ 265 """
62 Check the permission of files 266 Check the permission of files
63 """ 267 """
64 pass 268 sane = True
269 return sane
65 270
66def package_qa_check_arch(path,name,d): 271def package_qa_check_arch(path,name,d):
67 """ 272 """
68 Check if archs are compatible 273 Check if archs are compatible
69 """ 274 """
70 pass 275 import bb, os
276 target_os = bb.data.getVar('TARGET_OS', d, True)
277 target_arch = bb.data.getVar('TARGET_ARCH', d, True)
278
279 # FIXME: Cross package confuse this check, so just skip them
280 if bb.data.inherits_class('cross', d) or bb.data.inherits_class('sdk', d):
281 return True
282
283 # avoid following links to /usr/bin (e.g. on udev builds)
284 # we will check the files pointed to anyway...
285 if os.path.islink(path):
286 return True
287
288 #if this will throw an exception, then fix the dict above
289 (machine, osabi, abiversion, littleendian, bits32) = package_qa_get_machine_dict()[target_os][target_arch]
290 elf = package_qa_get_elf(path, bits32)
291 try:
292 elf.open()
293 except:
294 # just for debbugging to check the parser, remove once convinced...
295 return True
296
297 if not machine == elf.machine():
298 bb.error("Architecture did not match (%d to %d) on %s" %(machine, elf.machine(), package_qa_clean_path(path,d)))
299 return not package_qa_make_fatal_error( 4, name, path, d )
300 elif not littleendian == elf.isLittleEndian():
301 bb.error("Endiannes did not match (%d to %d) on %s" % (littleendian, elf.isLittleEndian(), package_qa_clean_path(path,d)))
302 return not package_qa_make_fatal_error( 4, name, path, d )
303
304 return True
71 305
72def package_qa_check_pcla(path,name,d): 306def package_qa_check_pcla(path,name,d):
73 """ 307 """
74 .pc and .la files should not point 308 .pc and .la files should not point to the WORKDIR
75 """ 309 """
310 sane = True
311 return sane
76 312
77def package_qa_check_staged(path,d): 313def package_qa_check_staged(path,d):
78 """ 314 """
79 Check staged la and pc files for sanity 315 Check staged la and pc files for sanity
80 -e.g. installed being false 316 -e.g. installed being false
317
318 As this is run after every stage we should be able
319 to find the one responsible for the errors easily even
320 if we look at every .pc and .la file
81 """ 321 """
82 pass 322 import os, bb
323
324 sane = True
325 workdir = os.path.join(bb.data.getVar('TMPDIR', d, True), "work")
326
327 if bb.data.inherits_class("native", d):
328 installed = "installed=no"
329 else:
330 installed = "installed=yes"
331
332 # find all .la and .pc files
333 # read the content
334 # and check for stuff that looks wrong
335 for root, dirs, files in os.walk(path):
336 for file in files:
337 path = os.path.join(root,file)
338 if file[-2:] == "la":
339 file_content = open(path).read()
340 if installed in file_content or workdir in file_content:
341 bb.error("QA issue: %s failed sanity test (reference to workdir or installed)" % file )
342 if package_qa_make_fatal_error( 5, "staging", path, d):
343 sane = True
344 elif file[-2:] == "pc":
345 file_content = open(path).read()
346 if workdir in file_content:
347 bb.error("QA issue: %s failed sanity test (reference to workdir)" % file )
348 if package_qa_make_fatal_error( 6, "staging", path, d):
349 sane = False
350
351 return sane
83 352
84# Walk over all files in a directory and call func 353# Walk over all files in a directory and call func
85def package_qa_walk(path, funcs, package,d): 354def package_qa_walk(path, funcs, package,d):
86 import os 355 import os
356 sane = True
357
87 for root, dirs, files in os.walk(path): 358 for root, dirs, files in os.walk(path):
88 for file in files: 359 for file in files:
89 path = os.path.join(root,file) 360 path = os.path.join(root,file)
90 for func in funcs: 361 for func in funcs:
91 func(path, package, d) 362 if not func(path, package,d):
363 sane = False
364
365 return sane
92 366
93 367
94def package_qa_check_rdepends(pkg, workdir, d): 368def package_qa_check_rdepends(pkg, workdir, d):
95 import bb 369 import bb
370 sane = True
96 if not "-dbg" in pkg and not "task-" in pkg and not "-image" in pkg: 371 if not "-dbg" in pkg and not "task-" in pkg and not "-image" in pkg:
97 # Copied from package_ipk.bbclass 372 # Copied from package_ipk.bbclass
98 # boiler plate to update the data 373 # boiler plate to update the data
@@ -121,7 +396,12 @@ def package_qa_check_rdepends(pkg, workdir, d):
121 # Now do the sanity check!!! 396 # Now do the sanity check!!!
122 for rdepend in rdepends: 397 for rdepend in rdepends:
123 if "-dbg" in rdepend: 398 if "-dbg" in rdepend:
124 bb.fatal("QA issue, koen give us a better msg!!!") 399 package_qa_write_error( 2, pkgname, rdepend, d )
400 bb.error("QA issue: %s rdepends on %s" % (pkgname,rdepend))
401 if package_qa_make_fatal_error( 2, pkgname, rdepend, d ):
402 sane = False
403
404 return sane
125 405
126# The PACKAGE FUNC to scan each package 406# The PACKAGE FUNC to scan each package
127python do_package_qa () { 407python do_package_qa () {
@@ -133,22 +413,42 @@ python do_package_qa () {
133 if not packages: 413 if not packages:
134 return 414 return
135 415
416 walk_sane = True
417 rdepends_sane = True
136 for package in packages.split(): 418 for package in packages.split():
137 if bb.data.getVar('INSANE_SKIP_' + package, d, True): 419 if bb.data.getVar('INSANE_SKIP_' + package, d, True):
138 bb.note("Package: %s (skipped)" % package) 420 bb.note("Package: %s (skipped)" % package)
139 continue 421 continue
140 422
141 bb.note("Package: %s" % package) 423 bb.note("Checking Package: %s" % package)
142 path = "%s/install/%s" % (workdir, package) 424 path = "%s/install/%s" % (workdir, package)
143 package_qa_walk(path, [package_qa_check_rpath, package_qa_check_devdbg, package_qa_check_perm, package_qa_check_arch], package, d) 425 if not package_qa_walk(path, [package_qa_check_rpath, package_qa_check_devdbg, package_qa_check_perm, package_qa_check_arch], package, d):
144 package_qa_check_rdepends(package, workdir, d) 426 walk_sane = False
427 if not package_qa_check_rdepends(package, workdir, d):
428 rdepends_sane = False
429
430 if not walk_sane or not rdepends_sane:
431 bb.fatal("QA run found fatal errors. Please consider fixing them.")
432 bb.note("DONE with PACKAGE QA")
145} 433}
146 434
147 435
148# The Staging Func, to check all staging 436# The Staging Func, to check all staging
149addtask qa_staging after do_populate_staging before do_build 437addtask qa_staging after do_populate_staging before do_build
150python do_qa_staging() { 438python do_qa_staging() {
151 bb.note("Staged!") 439 bb.note("QA checking staging")
152 440
153 package_qa_check_staged(bb.data.getVar('STAGING_DIR',d,True), d) 441 if not package_qa_check_staged(bb.data.getVar('STAGING_LIBDIR',d,True), d):
442 bb.fatal("QA staging was broken by the package built above")
443}
444
445# Check broken config.log files
446addtask qa_configure after do_configure before do_compile
447python do_qa_configure() {
448 bb.note("Checking sanity of the config.log file")
449 import os
450 for root, dirs, files in os.walk(bb.data.getVar('WORKDIR', d, True)):
451 if "config.log" in files:
452 if os.system("grep 'CROSS COMPILE Badness:' %s > /dev/null" % (os.path.join(root,"config.log"))) == 0:
453 bb.fatal("This autoconf log indicates errors, it looked at host includes. Rerun configure task after fixing this. Path was '%s'" % root)
154} 454}