summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/cases/reproducible.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/selftest/cases/reproducible.py')
-rw-r--r--meta/lib/oeqa/selftest/cases/reproducible.py77
1 files changed, 41 insertions, 36 deletions
diff --git a/meta/lib/oeqa/selftest/cases/reproducible.py b/meta/lib/oeqa/selftest/cases/reproducible.py
index 0d0259477e..80e830136f 100644
--- a/meta/lib/oeqa/selftest/cases/reproducible.py
+++ b/meta/lib/oeqa/selftest/cases/reproducible.py
@@ -9,35 +9,13 @@ import bb.utils
9import functools 9import functools
10import multiprocessing 10import multiprocessing
11import textwrap 11import textwrap
12import json
13import unittest
14import tempfile 12import tempfile
15import shutil 13import shutil
16import stat 14import stat
17import os 15import os
18import datetime 16import datetime
19 17
20# For sample packages, see:
21# https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20201127-0t7wr_oo/
22# https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20201127-4s9ejwyp/
23# https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20201127-haiwdlbr/
24# https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20201127-hwds3mcl/
25# https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20201203-sua0pzvc/
26# (both packages/ and packages-excluded/)
27
28# ruby-ri-docs, meson:
29#https://autobuilder.yocto.io/pub/repro-fail/oe-reproducible-20210215-0_td9la2/packages/diff-html/
30exclude_packages = [ 18exclude_packages = [
31 'glide',
32 'go-dep',
33 'go-helloworld',
34 'go-runtime',
35 'go_',
36 'go-',
37 'meson',
38 'ovmf-shell-efi',
39 'perf',
40 'ruby-ri-docs'
41 ] 19 ]
42 20
43def is_excluded(package): 21def is_excluded(package):
@@ -65,13 +43,14 @@ class CompareResult(object):
65 return (self.status, self.test) < (other.status, other.test) 43 return (self.status, self.test) < (other.status, other.test)
66 44
67class PackageCompareResults(object): 45class PackageCompareResults(object):
68 def __init__(self): 46 def __init__(self, exclusions):
69 self.total = [] 47 self.total = []
70 self.missing = [] 48 self.missing = []
71 self.different = [] 49 self.different = []
72 self.different_excluded = [] 50 self.different_excluded = []
73 self.same = [] 51 self.same = []
74 self.active_exclusions = set() 52 self.active_exclusions = set()
53 exclude_packages.extend((exclusions or "").split())
75 54
76 def add_result(self, r): 55 def add_result(self, r):
77 self.total.append(r) 56 self.total.append(r)
@@ -118,8 +97,9 @@ def compare_file(reference, test, diffutils_sysroot):
118 result.status = SAME 97 result.status = SAME
119 return result 98 return result
120 99
121def run_diffoscope(a_dir, b_dir, html_dir, **kwargs): 100def run_diffoscope(a_dir, b_dir, html_dir, max_report_size=0, **kwargs):
122 return runCmd(['diffoscope', '--no-default-limits', '--exclude-directory-metadata', 'yes', '--html-dir', html_dir, a_dir, b_dir], 101 return runCmd(['diffoscope', '--no-default-limits', '--max-report-size', str(max_report_size),
102 '--exclude-directory-metadata', 'yes', '--html-dir', html_dir, a_dir, b_dir],
123 **kwargs) 103 **kwargs)
124 104
125class DiffoscopeTests(OESelftestTestCase): 105class DiffoscopeTests(OESelftestTestCase):
@@ -149,10 +129,15 @@ class ReproducibleTests(OESelftestTestCase):
149 129
150 package_classes = ['deb', 'ipk', 'rpm'] 130 package_classes = ['deb', 'ipk', 'rpm']
151 131
132 # Maximum report size, in bytes
133 max_report_size = 250 * 1024 * 1024
134
152 # targets are the things we want to test the reproducibility of 135 # targets are the things we want to test the reproducibility of
153 targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world'] 136 targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world']
137
154 # sstate targets are things to pull from sstate to potentially cut build/debugging time 138 # sstate targets are things to pull from sstate to potentially cut build/debugging time
155 sstate_targets = [] 139 sstate_targets = []
140
156 save_results = False 141 save_results = False
157 if 'OEQA_DEBUGGING_SAVED_OUTPUT' in os.environ: 142 if 'OEQA_DEBUGGING_SAVED_OUTPUT' in os.environ:
158 save_results = os.environ['OEQA_DEBUGGING_SAVED_OUTPUT'] 143 save_results = os.environ['OEQA_DEBUGGING_SAVED_OUTPUT']
@@ -167,11 +152,29 @@ class ReproducibleTests(OESelftestTestCase):
167 152
168 def setUpLocal(self): 153 def setUpLocal(self):
169 super().setUpLocal() 154 super().setUpLocal()
170 needed_vars = ['TOPDIR', 'TARGET_PREFIX', 'BB_NUMBER_THREADS'] 155 needed_vars = [
156 'TOPDIR',
157 'TARGET_PREFIX',
158 'BB_NUMBER_THREADS',
159 'BB_HASHSERVE',
160 'OEQA_REPRODUCIBLE_TEST_PACKAGE',
161 'OEQA_REPRODUCIBLE_TEST_TARGET',
162 'OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS',
163 'OEQA_REPRODUCIBLE_EXCLUDED_PACKAGES',
164 ]
171 bb_vars = get_bb_vars(needed_vars) 165 bb_vars = get_bb_vars(needed_vars)
172 for v in needed_vars: 166 for v in needed_vars:
173 setattr(self, v.lower(), bb_vars[v]) 167 setattr(self, v.lower(), bb_vars[v])
174 168
169 if bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE']:
170 self.package_classes = bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE'].split()
171
172 if bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET']:
173 self.targets = bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET'].split()
174
175 if bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS']:
176 self.sstate_targets = bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS'].split()
177
175 self.extraresults = {} 178 self.extraresults = {}
176 self.extraresults.setdefault('reproducible.rawlogs', {})['log'] = '' 179 self.extraresults.setdefault('reproducible.rawlogs', {})['log'] = ''
177 self.extraresults.setdefault('reproducible', {}).setdefault('files', {}) 180 self.extraresults.setdefault('reproducible', {}).setdefault('files', {})
@@ -180,7 +183,7 @@ class ReproducibleTests(OESelftestTestCase):
180 self.extraresults['reproducible.rawlogs']['log'] += msg 183 self.extraresults['reproducible.rawlogs']['log'] += msg
181 184
182 def compare_packages(self, reference_dir, test_dir, diffutils_sysroot): 185 def compare_packages(self, reference_dir, test_dir, diffutils_sysroot):
183 result = PackageCompareResults() 186 result = PackageCompareResults(self.oeqa_reproducible_excluded_packages)
184 187
185 old_cwd = os.getcwd() 188 old_cwd = os.getcwd()
186 try: 189 try:
@@ -219,12 +222,10 @@ class ReproducibleTests(OESelftestTestCase):
219 bb.utils.remove(tmpdir, recurse=True) 222 bb.utils.remove(tmpdir, recurse=True)
220 223
221 config = textwrap.dedent('''\ 224 config = textwrap.dedent('''\
222 INHERIT += "reproducible_build"
223 PACKAGE_CLASSES = "{package_classes}" 225 PACKAGE_CLASSES = "{package_classes}"
224 INHIBIT_PACKAGE_STRIP = "1"
225 TMPDIR = "{tmpdir}" 226 TMPDIR = "{tmpdir}"
226 LICENSE_FLAGS_WHITELIST = "commercial" 227 LICENSE_FLAGS_ACCEPTED = "commercial"
227 DISTRO_FEATURES_append = ' systemd pam' 228 DISTRO_FEATURES:append = ' pam'
228 USERADDEXTENSION = "useradd-staticids" 229 USERADDEXTENSION = "useradd-staticids"
229 USERADD_ERROR_DYNAMIC = "skip" 230 USERADD_ERROR_DYNAMIC = "skip"
230 USERADD_UID_TABLES += "files/static-passwd" 231 USERADD_UID_TABLES += "files/static-passwd"
@@ -242,7 +243,7 @@ class ReproducibleTests(OESelftestTestCase):
242 # mirror, forcing a complete build from scratch 243 # mirror, forcing a complete build from scratch
243 config += textwrap.dedent('''\ 244 config += textwrap.dedent('''\
244 SSTATE_DIR = "${TMPDIR}/sstate" 245 SSTATE_DIR = "${TMPDIR}/sstate"
245 SSTATE_MIRRORS = "" 246 SSTATE_MIRRORS = "file://.*/.*-native.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH file://.*/.*-cross.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH"
246 ''') 247 ''')
247 248
248 self.logger.info("Building %s (sstate%s allowed)..." % (name, '' if use_sstate else ' NOT')) 249 self.logger.info("Building %s (sstate%s allowed)..." % (name, '' if use_sstate else ' NOT'))
@@ -309,9 +310,13 @@ class ReproducibleTests(OESelftestTestCase):
309 self.copy_file(d.reference, '/'.join([save_dir, 'packages-excluded', strip_topdir(d.reference)])) 310 self.copy_file(d.reference, '/'.join([save_dir, 'packages-excluded', strip_topdir(d.reference)]))
310 self.copy_file(d.test, '/'.join([save_dir, 'packages-excluded', strip_topdir(d.test)])) 311 self.copy_file(d.test, '/'.join([save_dir, 'packages-excluded', strip_topdir(d.test)]))
311 312
312 if result.missing or result.different: 313 if result.different:
313 fails.append("The following %s packages are missing or different and not in exclusion list: %s" % 314 fails.append("The following %s packages are different and not in exclusion list:\n%s" %
314 (c, '\n'.join(r.test for r in (result.missing + result.different)))) 315 (c, '\n'.join(r.test for r in (result.different))))
316
317 if result.missing and len(self.sstate_targets) == 0:
318 fails.append("The following %s packages are missing and not in exclusion list:\n%s" %
319 (c, '\n'.join(r.test for r in (result.missing))))
315 320
316 # Clean up empty directories 321 # Clean up empty directories
317 if self.save_results: 322 if self.save_results:
@@ -325,7 +330,7 @@ class ReproducibleTests(OESelftestTestCase):
325 # Copy jquery to improve the diffoscope output usability 330 # Copy jquery to improve the diffoscope output usability
326 self.copy_file(os.path.join(jquery_sysroot, 'usr/share/javascript/jquery/jquery.min.js'), os.path.join(package_html_dir, 'jquery.js')) 331 self.copy_file(os.path.join(jquery_sysroot, 'usr/share/javascript/jquery/jquery.min.js'), os.path.join(package_html_dir, 'jquery.js'))
327 332
328 run_diffoscope('reproducibleA', 'reproducibleB', package_html_dir, 333 run_diffoscope('reproducibleA', 'reproducibleB', package_html_dir, max_report_size=self.max_report_size,
329 native_sysroot=diffoscope_sysroot, ignore_status=True, cwd=package_dir) 334 native_sysroot=diffoscope_sysroot, ignore_status=True, cwd=package_dir)
330 335
331 if fails: 336 if fails: