Merge ~ubuntu-release/autopkgtest/+git/development:cross-arch-testing into ~ubuntu-release/autopkgtest/+git/development:master
- Git
- lp:~ubuntu-release/autopkgtest/+git/development
- cross-arch-testing
- Merge into master
Proposed by
Steve Langasek
Status: | Merged |
---|---|
Merge reported by: | Brian Murray |
Merged at revision: | c4fc7003a2447e07d4e44083757dfe3458b26e2e |
Proposed branch: | ~ubuntu-release/autopkgtest/+git/development:cross-arch-testing |
Merge into: | ~ubuntu-release/autopkgtest/+git/development:master |
Diff against target: |
533 lines (+247/-69) 8 files modified
.gitlab-ci.yml (+2/-2) debian/control (+26/-24) debian/tests/control (+6/-9) lib/adt_testbed.py (+119/-2) lib/autopkgtest_args.py (+5/-0) lib/testdesc.py (+28/-10) runner/autopkgtest (+55/-22) runner/autopkgtest.1 (+6/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Julian Andres Klode (community) | Approve | ||
Iain Lane | Pending | ||
Review via email: mp+376169@code.launchpad.net |
Commit message
Description of the change
A first pass at implementing a -a $arch argument to autopkgtest to support cross-arch testing
To post a comment you must log in.
- c4fc700... by Steve Langasek
-
coding style
Revision history for this message
Julian Andres Klode (juliank) wrote : | # |
Revision history for this message
Steve Langasek (vorlon) : | # |
Revision history for this message
Julian Andres Klode (juliank) : | # |
Revision history for this message
Julian Andres Klode (juliank) : | # |
Revision history for this message
Julian Andres Klode (juliank) wrote : | # |
The code seems to do the right thing when testing it, but see the previous note about the missing = in the man page.
review:
Approve
Revision history for this message
Brian Murray (brian-murray) wrote : | # |
All the commit ids in this merge proposal exist in the master branch of autopkgtest so I'm setting this to merged.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml | |||
2 | index f28fa86..b9e1492 100644 | |||
3 | --- a/.gitlab-ci.yml | |||
4 | +++ b/.gitlab-ci.yml | |||
5 | @@ -17,9 +17,9 @@ tests-sid: | |||
6 | 17 | - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian | 17 | - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian |
7 | 18 | - tests/autopkgtest NullRunner NullRunnerRoot ChrootRunner | 18 | - tests/autopkgtest NullRunner NullRunnerRoot ChrootRunner |
8 | 19 | 19 | ||
10 | 20 | tests-stretch: | 20 | tests-stable: |
11 | 21 | stage: test | 21 | stage: test |
13 | 22 | image: debian:stretch | 22 | image: debian:stable |
14 | 23 | script: | 23 | script: |
15 | 24 | - apt-get update | 24 | - apt-get update |
16 | 25 | - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian | 25 | - apt-get install -y apt-utils autodep8 build-essential debhelper libdpkg-perl procps python3 python3-debian |
17 | diff --git a/debian/control b/debian/control | |||
18 | index 6b347f0..c37b4ee 100644 | |||
19 | --- a/debian/control | |||
20 | +++ b/debian/control | |||
21 | @@ -1,39 +1,41 @@ | |||
22 | 1 | Source: autopkgtest | 1 | Source: autopkgtest |
23 | 2 | Maintainer: Debian CI team <team+ci@tracker.debian.org> | 2 | Maintainer: Debian CI team <team+ci@tracker.debian.org> |
25 | 3 | Uploaders: Ian Jackson <ijackson@chiark.greenend.org.uk>, Martin Pitt <mpitt@debian.org>, Antonio Terceiro <terceiro@debian.org>, Paul Gevers <elbrus@debian.org> | 3 | Uploaders: Ian Jackson <ijackson@chiark.greenend.org.uk>, |
26 | 4 | Martin Pitt <mpitt@debian.org>, | ||
27 | 5 | Antonio Terceiro <terceiro@debian.org>, | ||
28 | 6 | Paul Gevers <elbrus@debian.org> | ||
29 | 4 | Section: devel | 7 | Section: devel |
30 | 5 | Priority: optional | 8 | Priority: optional |
31 | 6 | Standards-Version: 4.4.0 | 9 | Standards-Version: 4.4.0 |
32 | 7 | Build-Depends: debhelper (>= 9), | 10 | Build-Depends: debhelper (>= 9), |
40 | 8 | python3 (>= 3.1), | 11 | procps, |
41 | 9 | python3-mock, | 12 | pycodestyle | pep8, |
42 | 10 | python3-debian, | 13 | pyflakes3 | pyflakes, |
43 | 11 | python3-docutils, | 14 | python3 (>= 3.1), |
44 | 12 | pyflakes3 | pyflakes, | 15 | python3-debian, |
45 | 13 | procps, | 16 | python3-docutils, |
46 | 14 | pycodestyle | pep8, | 17 | python3-mock |
47 | 15 | Vcs-Git: https://salsa.debian.org/ci-team/autopkgtest.git | 18 | Vcs-Git: https://salsa.debian.org/ci-team/autopkgtest.git |
48 | 16 | Vcs-Browser: https://salsa.debian.org/ci-team/autopkgtest | 19 | Vcs-Browser: https://salsa.debian.org/ci-team/autopkgtest |
49 | 17 | 20 | ||
50 | 18 | Package: autopkgtest | 21 | Package: autopkgtest |
51 | 19 | Architecture: all | 22 | Architecture: all |
58 | 20 | Depends: python3, | 23 | Depends: apt-utils, |
59 | 21 | python3-debian, | 24 | libdpkg-perl, |
60 | 22 | apt-utils, | 25 | procps, |
61 | 23 | libdpkg-perl, | 26 | python3, |
62 | 24 | procps, | 27 | python3-debian, |
63 | 25 | ${misc:Depends} | 28 | ${misc:Depends} |
64 | 26 | Recommends: autodep8 | 29 | Recommends: autodep8 |
75 | 27 | Suggests: | 30 | Suggests: lxc, |
76 | 28 | lxc, | 31 | lxd, |
77 | 29 | lxd, | 32 | ovmf, |
78 | 30 | ovmf, | 33 | qemu-efi-aarch64, |
79 | 31 | qemu-efi-aarch64, | 34 | qemu-efi-arm, |
80 | 32 | qemu-efi-arm, | 35 | qemu-system, |
81 | 33 | qemu-system, | 36 | qemu-utils, |
82 | 34 | qemu-utils, | 37 | schroot, |
83 | 35 | schroot, | 38 | vmdb2 |
74 | 36 | vmdb2, | ||
84 | 37 | Breaks: debci (<< 1.7~) | 39 | Breaks: debci (<< 1.7~) |
85 | 38 | Description: automatic as-installed testing for Debian packages | 40 | Description: automatic as-installed testing for Debian packages |
86 | 39 | autopkgtest runs tests on binary packages. The tests are run on the | 41 | autopkgtest runs tests on binary packages. The tests are run on the |
87 | diff --git a/debian/tests/control b/debian/tests/control | |||
88 | index a59b3ef..26f4a18 100644 | |||
89 | --- a/debian/tests/control | |||
90 | +++ b/debian/tests/control | |||
91 | @@ -1,8 +1,5 @@ | |||
92 | 1 | Tests: autopkgtest | 1 | Tests: autopkgtest |
97 | 2 | Depends: autopkgtest, | 2 | Depends: autodep8, autopkgtest, build-essential, debhelper (>= 7) |
94 | 3 | autodep8, | ||
95 | 4 | build-essential, | ||
96 | 5 | debhelper (>= 7) | ||
98 | 6 | Restrictions: needs-root | 3 | Restrictions: needs-root |
99 | 7 | Tests-Directory: tests | 4 | Tests-Directory: tests |
100 | 8 | 5 | ||
101 | @@ -15,9 +12,9 @@ Depends: autopkgtest | |||
102 | 15 | 12 | ||
103 | 16 | Tests: lxd | 13 | Tests: lxd |
104 | 17 | Depends: autopkgtest, | 14 | Depends: autopkgtest, |
110 | 18 | lxd, | 15 | build-essential, |
111 | 19 | build-essential, | 16 | debhelper (>= 7), |
112 | 20 | debhelper (>= 7), | 17 | fakeroot, |
113 | 21 | fakeroot, | 18 | iptables, |
114 | 22 | iptables | 19 | lxd |
115 | 23 | Restrictions: isolation-machine, needs-root, allow-stderr | 20 | Restrictions: isolation-machine, needs-root, allow-stderr |
116 | diff --git a/lib/adt_testbed.py b/lib/adt_testbed.py | |||
117 | index 4fe8874..ff1a0ff 100644 | |||
118 | --- a/lib/adt_testbed.py | |||
119 | +++ b/lib/adt_testbed.py | |||
120 | @@ -395,7 +395,8 @@ class Testbed: | |||
121 | 395 | self._opened(pl) | 395 | self._opened(pl) |
122 | 396 | self.modified = False | 396 | self.modified = False |
123 | 397 | 397 | ||
125 | 398 | def install_deps(self, deps_new, recommends, shell_on_failure=False, synth_deps=[]): | 398 | def install_deps(self, deps_new, recommends, shell_on_failure=False, |
126 | 399 | synth_deps=[], cross_arch=None): | ||
127 | 399 | '''Install dependencies into testbed''' | 400 | '''Install dependencies into testbed''' |
128 | 400 | adtlog.debug('install_deps: deps_new=%s, recommends=%s' % (deps_new, recommends)) | 401 | adtlog.debug('install_deps: deps_new=%s, recommends=%s' % (deps_new, recommends)) |
129 | 401 | 402 | ||
130 | @@ -403,7 +404,24 @@ class Testbed: | |||
131 | 403 | self.recommends_installed = recommends | 404 | self.recommends_installed = recommends |
132 | 404 | if not deps_new: | 405 | if not deps_new: |
133 | 405 | return | 406 | return |
135 | 406 | self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', recommends, shell_on_failure=shell_on_failure, synth_deps=synth_deps) | 407 | if cross_arch: |
136 | 408 | # mock up a .dsc with our dependencies string as our build-deps | ||
137 | 409 | dsc = TempPath(self, 'autopkgtest-satdep.dsc') | ||
138 | 410 | with open(dsc.host, 'w', encoding='UTF-8') as f_dsc: | ||
139 | 411 | f_dsc.write("Source: autopkgtest-satdep\n" | ||
140 | 412 | "Binary: autopkgtest-satdep\n" | ||
141 | 413 | "Architecture: any all\n" | ||
142 | 414 | "Build-Depends: " + ', '.join(deps_new) + "\n") | ||
143 | 415 | dsc.copydown() | ||
144 | 416 | |||
145 | 417 | # feed the result to apt-get build-dep | ||
146 | 418 | self.satisfy_build_deps(dsc.tb, cross_arch, | ||
147 | 419 | recommends, shell_on_failure) | ||
148 | 420 | # Now handle the synthetic deps as well | ||
149 | 421 | self.install_apt('', recommends, shell_on_failure, synth_deps) | ||
150 | 422 | |||
151 | 423 | else: | ||
152 | 424 | self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', recommends, shell_on_failure=shell_on_failure, synth_deps=synth_deps) | ||
153 | 407 | 425 | ||
154 | 408 | def needs_reset(self): | 426 | def needs_reset(self): |
155 | 409 | # show what caused a reset | 427 | # show what caused a reset |
156 | @@ -1021,6 +1039,105 @@ fi | |||
157 | 1021 | if self.execute(['sh', adtlog.verbosity >= 2 and '-exc' or '-ec', script], kind='install')[0] != 0: | 1039 | if self.execute(['sh', adtlog.verbosity >= 2 and '-exc' or '-ec', script], kind='install')[0] != 0: |
158 | 1022 | self.bomb('Failed to update click AppArmor rules') | 1040 | self.bomb('Failed to update click AppArmor rules') |
159 | 1023 | 1041 | ||
160 | 1042 | def _run_apt_build_dep(self, what, prefix, recommends, ignorerc=False): | ||
161 | 1043 | '''actually run apt-get build-dep''' | ||
162 | 1044 | |||
163 | 1045 | # capture status-fd to stderr | ||
164 | 1046 | (rc, _, serr) = self.execute(['/bin/sh', '-ec', '%s apt-get build-dep ' | ||
165 | 1047 | '--assume-yes %s ' | ||
166 | 1048 | '-o APT::Status-Fd=3 ' | ||
167 | 1049 | '-o APT::Install-Recommends=%s ' | ||
168 | 1050 | '-o Dpkg::Options::=--force-confnew ' | ||
169 | 1051 | '-o Debug::pkgProblemResolver=true 3>&2 2>&1' % | ||
170 | 1052 | (prefix, what, recommends)], | ||
171 | 1053 | kind='install', stderr=subprocess.PIPE) | ||
172 | 1054 | if not ignorerc and rc != 0: | ||
173 | 1055 | adtlog.debug('apt-get build-dep %s failed; status-fd:\n%s' % (what, serr)) | ||
174 | 1056 | # check if apt failed during package download, which might be a | ||
175 | 1057 | # transient error, so retry | ||
176 | 1058 | if 'dlstatus:' in serr and 'pmstatus:' not in serr: | ||
177 | 1059 | raise adtlog.AptDownloadError | ||
178 | 1060 | else: | ||
179 | 1061 | raise adtlog.AptPermanentError | ||
180 | 1062 | |||
181 | 1063 | def satisfy_build_deps(self, source_pkg, architecture=None, | ||
182 | 1064 | recommends=False, shell_on_failure=False): | ||
183 | 1065 | '''Install build-dependencies into testbed with apt-get build-dep | ||
184 | 1066 | |||
185 | 1067 | This requires root privileges and a writable file system. | ||
186 | 1068 | ''' | ||
187 | 1069 | # check if we can use apt-get | ||
188 | 1070 | can_apt_get = False | ||
189 | 1071 | if 'root-on-testbed' in self.caps: | ||
190 | 1072 | rc = self.execute(['test', '-w', '/var/lib/dpkg/status'])[0] | ||
191 | 1073 | if rc == 0: | ||
192 | 1074 | can_apt_get = True | ||
193 | 1075 | adtlog.debug('can use apt-get on testbed: %s' % can_apt_get) | ||
194 | 1076 | |||
195 | 1077 | if not can_apt_get: | ||
196 | 1078 | self.bomb('no root on testbed, unsupported when specifying target architecture') | ||
197 | 1079 | |||
198 | 1080 | what = source_pkg | ||
199 | 1081 | if architecture: | ||
200 | 1082 | what += ' -a ' + architecture | ||
201 | 1083 | # install the build-dependencies for the specified source (which can | ||
202 | 1084 | # be a source package name, or a path); our apt pinning is not | ||
203 | 1085 | # very clever wrt. resolving transitional dependencies in the pocket, | ||
204 | 1086 | # so we might need to retry without pinning | ||
205 | 1087 | download_fail_retries = 3 | ||
206 | 1088 | while True: | ||
207 | 1089 | rc = 0 | ||
208 | 1090 | try: | ||
209 | 1091 | self._run_apt_build_dep(what, ' '.join(self.eatmydata_prefix), recommends) | ||
210 | 1092 | |||
211 | 1093 | # check if apt failed during package download, which might be a | ||
212 | 1094 | # transient error, so retry | ||
213 | 1095 | except adtlog.AptDownloadError: | ||
214 | 1096 | download_fail_retries -= 1 | ||
215 | 1097 | if download_fail_retries > 0: | ||
216 | 1098 | adtlog.warning('apt failed to download packages, retrying in 10s...') | ||
217 | 1099 | time.sleep(10) | ||
218 | 1100 | continue | ||
219 | 1101 | else: | ||
220 | 1102 | self.bomb('apt repeatedly failed to download packages') | ||
221 | 1103 | |||
222 | 1104 | except adtlog.AptPermanentError: | ||
223 | 1105 | rc = -1 | ||
224 | 1106 | if shell_on_failure: | ||
225 | 1107 | self.run_shell() | ||
226 | 1108 | |||
227 | 1109 | if rc != 0: | ||
228 | 1110 | if self.apt_pin_for_releases and self.enable_apt_fallback: | ||
229 | 1111 | release = self.apt_pin_for_releases.pop() | ||
230 | 1112 | adtlog.warning('Test dependencies are unsatisfiable using apt pinning. ' | ||
231 | 1113 | 'Retrying using all packages from %s' % release) | ||
232 | 1114 | self.check_exec(['/bin/sh', '-ec', 'rm /etc/apt/preferences.d/autopkgtest-' + release]) | ||
233 | 1115 | if not self.apt_pin_for_releases: | ||
234 | 1116 | self.check_exec(['/bin/sh', '-ec', 'rm -f /etc/apt/preferences.d/autopkgtest-default-release']) | ||
235 | 1117 | continue | ||
236 | 1118 | |||
237 | 1119 | adtlog.warning('Test dependencies are unsatisfiable - calling ' | ||
238 | 1120 | 'apt install on test deps directly for further ' | ||
239 | 1121 | 'data about failing dependencies in test logs') | ||
240 | 1122 | self._run_apt_build_dep('--simulate ' + what, | ||
241 | 1123 | ' '.join(self.eatmydata_prefix), | ||
242 | 1124 | recommends, ignorerc=True) | ||
243 | 1125 | |||
244 | 1126 | if shell_on_failure: | ||
245 | 1127 | self.run_shell() | ||
246 | 1128 | if self.enable_apt_fallback: | ||
247 | 1129 | self.badpkg('Test dependencies are unsatisfiable. A common reason is ' | ||
248 | 1130 | 'that your testbed is out of date with respect to the ' | ||
249 | 1131 | 'archive, and you need to use a current testbed or run ' | ||
250 | 1132 | 'apt-get update or use -U.') | ||
251 | 1133 | else: | ||
252 | 1134 | self.badpkg('Test dependencies are unsatisfiable. A common reason is ' | ||
253 | 1135 | 'that the requested apt pinning prevented dependencies ' | ||
254 | 1136 | 'from the non-default suite to be installed. In that ' | ||
255 | 1137 | 'case you need to add those dependencies to the pinning ' | ||
256 | 1138 | 'list.') | ||
257 | 1139 | break | ||
258 | 1140 | |||
259 | 1024 | def satisfy_dependencies_string(self, deps, what, recommends=False, | 1141 | def satisfy_dependencies_string(self, deps, what, recommends=False, |
260 | 1025 | build_dep=False, shell_on_failure=False, synth_deps=[]): | 1142 | build_dep=False, shell_on_failure=False, synth_deps=[]): |
261 | 1026 | '''Install dependencies from a string into the testbed''' | 1143 | '''Install dependencies from a string into the testbed''' |
262 | diff --git a/lib/autopkgtest_args.py b/lib/autopkgtest_args.py | |||
263 | index 7fea2d8..9edb4a8 100644 | |||
264 | --- a/lib/autopkgtest_args.py | |||
265 | +++ b/lib/autopkgtest_args.py | |||
266 | @@ -210,6 +210,11 @@ for details.''' | |||
267 | 210 | help='Run tests from already installed click package ' | 210 | help='Run tests from already installed click package ' |
268 | 211 | '(e. g. "com.example.myapp"), from specified click ' | 211 | '(e. g. "com.example.myapp"), from specified click ' |
269 | 212 | 'source directory or manifest\'s x-source.') | 212 | 'source directory or manifest\'s x-source.') |
270 | 213 | g_test.add_argument('-a', '--architecture', metavar='ARCH', | ||
271 | 214 | help='run tests for (and when asked, build binaries ' | ||
272 | 215 | 'for) ARCH instead of the testbed host architecture. ' | ||
273 | 216 | 'Assumes ARCH is available as a foreign architecture ' | ||
274 | 217 | 'on the testbed.') | ||
275 | 213 | g_test.add_argument('packages', nargs='*', | 218 | g_test.add_argument('packages', nargs='*', |
276 | 214 | help='testsrc source package and testbinary packages as above') | 219 | help='testsrc source package and testbinary packages as above') |
277 | 215 | 220 | ||
278 | diff --git a/lib/testdesc.py b/lib/testdesc.py | |||
279 | index 84d24db..eb2ffd4 100644 | |||
280 | --- a/lib/testdesc.py | |||
281 | +++ b/lib/testdesc.py | |||
282 | @@ -239,7 +239,7 @@ def _debian_check_unknown_fields(name, record): | |||
283 | 239 | raise Unsupported(name, 'unknown field %s' % unknown_keys.pop()) | 239 | raise Unsupported(name, 'unknown field %s' % unknown_keys.pop()) |
284 | 240 | 240 | ||
285 | 241 | 241 | ||
287 | 242 | def _debian_packages_from_source(srcdir): | 242 | def _debian_packages_from_source(srcdir, cross_arch=None): |
288 | 243 | packages = [] | 243 | packages = [] |
289 | 244 | packages_no_arch = [] | 244 | packages_no_arch = [] |
290 | 245 | 245 | ||
291 | @@ -252,10 +252,17 @@ def _debian_packages_from_source(srcdir): | |||
292 | 252 | st.get('Package-type', 'deb') != 'deb': | 252 | st.get('Package-type', 'deb') != 'deb': |
293 | 253 | continue | 253 | continue |
294 | 254 | arch = st['Architecture'] | 254 | arch = st['Architecture'] |
296 | 255 | if arch in ('all', 'any'): | 255 | qual_pkg = st['Package'] |
297 | 256 | # take care to emit an arch qualifier only for arch-dependent | ||
298 | 257 | # packages, not for arch: all ones | ||
299 | 258 | if cross_arch: | ||
300 | 259 | qual_pkg += ':' + cross_arch | ||
301 | 260 | if arch == 'all': | ||
302 | 256 | packages.append(st['Package']) | 261 | packages.append(st['Package']) |
303 | 262 | elif arch == 'any': | ||
304 | 263 | packages.append(qual_pkg) | ||
305 | 257 | else: | 264 | else: |
307 | 258 | packages.append('%s [%s]' % (st['Package'], arch)) | 265 | packages.append('%s [%s]' % (qual_pkg, arch)) |
308 | 259 | packages_no_arch.append(st['Package']) | 266 | packages_no_arch.append(st['Package']) |
309 | 260 | 267 | ||
310 | 261 | return (packages, packages_no_arch) | 268 | return (packages, packages_no_arch) |
311 | @@ -353,7 +360,8 @@ def _synthesize_deps(dep, testbed_arch): | |||
312 | 353 | return None | 360 | return None |
313 | 354 | 361 | ||
314 | 355 | 362 | ||
316 | 356 | def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): | 363 | def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch, |
317 | 364 | cross_arch=None): | ||
318 | 357 | '''Parse Depends: line in a Debian package | 365 | '''Parse Depends: line in a Debian package |
319 | 358 | 366 | ||
320 | 359 | Split dependencies (comma separated), validate their syntax, and expand @ | 367 | Split dependencies (comma separated), validate their syntax, and expand @ |
321 | @@ -364,7 +372,12 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): | |||
322 | 364 | ''' | 372 | ''' |
323 | 365 | deps = [] | 373 | deps = [] |
324 | 366 | synthdeps = [] | 374 | synthdeps = [] |
326 | 367 | (my_packages, my_packages_no_arch) = _debian_packages_from_source(srcdir) | 375 | (my_packages, my_packages_no_arch) = _debian_packages_from_source(srcdir, |
327 | 376 | cross_arch=cross_arch) | ||
328 | 377 | if cross_arch: | ||
329 | 378 | target_arch = cross_arch | ||
330 | 379 | else: | ||
331 | 380 | target_arch = testbed_arch | ||
332 | 368 | for alt_group_str in dep_str.split(','): | 381 | for alt_group_str in dep_str.split(','): |
333 | 369 | alt_group_str = alt_group_str.strip() | 382 | alt_group_str = alt_group_str.strip() |
334 | 370 | if not alt_group_str: | 383 | if not alt_group_str: |
335 | @@ -375,7 +388,7 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): | |||
336 | 375 | for d in my_packages: | 388 | for d in my_packages: |
337 | 376 | adtlog.debug('synthesised dependency %s' % d) | 389 | adtlog.debug('synthesised dependency %s' % d) |
338 | 377 | deps.append(d) | 390 | deps.append(d) |
340 | 378 | s = _synthesize_deps(d, testbed_arch) | 391 | s = _synthesize_deps(d, target_arch) |
341 | 379 | if s: | 392 | if s: |
342 | 380 | synthdeps.append(s) | 393 | synthdeps.append(s) |
343 | 381 | elif alt_group_str == '@builddeps@': | 394 | elif alt_group_str == '@builddeps@': |
344 | @@ -389,8 +402,10 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): | |||
345 | 389 | if pkg not in my_packages_no_arch: | 402 | if pkg not in my_packages_no_arch: |
346 | 390 | synthdep_alternatives = [] | 403 | synthdep_alternatives = [] |
347 | 391 | break | 404 | break |
349 | 392 | s = _synthesize_deps(dep, testbed_arch) | 405 | s = _synthesize_deps(dep, target_arch) |
350 | 393 | if s: | 406 | if s: |
351 | 407 | if cross_arch: | ||
352 | 408 | s += ':' + cross_arch | ||
353 | 394 | synthdep_alternatives.append(s) | 409 | synthdep_alternatives.append(s) |
354 | 395 | if synthdep_alternatives: | 410 | if synthdep_alternatives: |
355 | 396 | adtlog.debug('marked alternatives %s as a synthesised dependency' % synthdep_alternatives) | 411 | adtlog.debug('marked alternatives %s as a synthesised dependency' % synthdep_alternatives) |
356 | @@ -398,6 +413,9 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): | |||
357 | 398 | synthdeps.append(synthdep_alternatives) | 413 | synthdeps.append(synthdep_alternatives) |
358 | 399 | else: | 414 | else: |
359 | 400 | synthdeps.append(synthdep_alternatives[0]) | 415 | synthdeps.append(synthdep_alternatives[0]) |
360 | 416 | if cross_arch and alt_group_str + ':' + cross_arch in my_packages: | ||
361 | 417 | adtlog.debug('%s is from our source package, adding arch qualifier for cross-testing' % s) | ||
362 | 418 | alt_group_str += ':' + cross_arch | ||
363 | 401 | deps.append(alt_group_str) | 419 | deps.append(alt_group_str) |
364 | 402 | 420 | ||
365 | 403 | return (deps, synthdeps) | 421 | return (deps, synthdeps) |
366 | @@ -429,7 +447,7 @@ def _autodep8(srcdir): | |||
367 | 429 | 447 | ||
368 | 430 | 448 | ||
369 | 431 | def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, | 449 | def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
371 | 432 | auto_control=True): | 450 | auto_control=True, cross_arch=None): |
372 | 433 | '''Parse test descriptions from a Debian DEP-8 source dir | 451 | '''Parse test descriptions from a Debian DEP-8 source dir |
373 | 434 | 452 | ||
374 | 435 | You can specify an alternative path for the control file (default: | 453 | You can specify an alternative path for the control file (default: |
375 | @@ -503,7 +521,7 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, | |||
376 | 503 | test_names[0], | 521 | test_names[0], |
377 | 504 | record.get('Depends', '@'), | 522 | record.get('Depends', '@'), |
378 | 505 | srcdir, | 523 | srcdir, |
380 | 506 | testbed_arch) | 524 | testbed_arch, cross_arch=cross_arch) |
381 | 507 | if 'Test-command' in record: | 525 | if 'Test-command' in record: |
382 | 508 | raise InvalidControl('*', 'Only one of "Tests" or ' | 526 | raise InvalidControl('*', 'Only one of "Tests" or ' |
383 | 509 | '"Test-Command" may be given') | 527 | '"Test-Command" may be given') |
384 | @@ -529,7 +547,7 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, | |||
385 | 529 | command, | 547 | command, |
386 | 530 | record.get('Depends', '@'), | 548 | record.get('Depends', '@'), |
387 | 531 | srcdir, | 549 | srcdir, |
389 | 532 | testbed_arch) | 550 | testbed_arch, cross_arch=cross_arch) |
390 | 533 | if feature_test_name is None: | 551 | if feature_test_name is None: |
391 | 534 | command_counter += 1 | 552 | command_counter += 1 |
392 | 535 | name = 'command%i' % command_counter | 553 | name = 'command%i' % command_counter |
393 | diff --git a/runner/autopkgtest b/runner/autopkgtest | |||
394 | index 93c6e61..f1e2e8f 100755 | |||
395 | --- a/runner/autopkgtest | |||
396 | +++ b/runner/autopkgtest | |||
397 | @@ -159,7 +159,10 @@ def run_tests(tests, tree): | |||
398 | 159 | binaries.publish() | 159 | binaries.publish() |
399 | 160 | doTest = True | 160 | doTest = True |
400 | 161 | try: | 161 | try: |
402 | 162 | testbed.install_deps(t.depends, 'needs-recommends' in t.restrictions, opts.shell_fail, t.synth_depends) | 162 | testbed.install_deps(t.depends, |
403 | 163 | 'needs-recommends' in t.restrictions, | ||
404 | 164 | opts.shell_fail, t.synth_depends, | ||
405 | 165 | opts.architecture) | ||
406 | 163 | except adtlog.BadPackageError as e: | 166 | except adtlog.BadPackageError as e: |
407 | 164 | if 'skip-not-installable' in t.restrictions: | 167 | if 'skip-not-installable' in t.restrictions: |
408 | 165 | errorcode |= 2 | 168 | errorcode |= 2 |
409 | @@ -388,6 +391,12 @@ def build_source(kind, arg, built_binaries): | |||
410 | 388 | return tests_tree | 391 | return tests_tree |
411 | 389 | 392 | ||
412 | 390 | elif kind == 'apt-source': | 393 | elif kind == 'apt-source': |
413 | 394 | # Make sure we are selecting the binaries based on the actual target | ||
414 | 395 | # architecture, not necessarily the testbed architecture | ||
415 | 396 | if opts.architecture: | ||
416 | 397 | arch = opts.architecture | ||
417 | 398 | else: | ||
418 | 399 | arch = testbed.dpkg_arch | ||
419 | 391 | # The default is to determine the version for "apt-get source | 400 | # The default is to determine the version for "apt-get source |
420 | 392 | # pkg=version" that conforms to the current apt pinning. We only | 401 | # pkg=version" that conforms to the current apt pinning. We only |
421 | 393 | # consider binaries which are shipped in all available versions, | 402 | # consider binaries which are shipped in all available versions, |
422 | @@ -443,7 +452,7 @@ pkgs=$(echo "$pkgs\n" | awk " | |||
423 | 443 | if (foundarch == 0 || archmatch == 1) thissrc[\\$1] = 1; | 452 | if (foundarch == 0 || archmatch == 1) thissrc[\\$1] = 1; |
424 | 444 | next } | 453 | next } |
425 | 445 | { if (!inlist) next; | 454 | { if (!inlist) next; |
427 | 446 | inlist=0;''' % {'src': arg, 'arch': testbed.dpkg_arch} | 455 | inlist=0;''' % {'src': arg, 'arch': arch} |
428 | 447 | 456 | ||
429 | 448 | create_command_part2_check_all_pkgs = ''' | 457 | create_command_part2_check_all_pkgs = ''' |
430 | 449 | remaining=0; | 458 | remaining=0; |
431 | @@ -580,24 +589,8 @@ dpkg-source -x %(src)s_*.dsc src >/dev/null''' % {'src': arg} | |||
432 | 580 | if kind not in ['dsc', 'apt-source']: | 589 | if kind not in ['dsc', 'apt-source']: |
433 | 581 | testbed.install_deps([], False) | 590 | testbed.install_deps([], False) |
434 | 582 | 591 | ||
435 | 583 | if kind in ('apt-source', 'git-source'): | ||
436 | 584 | # we need to get the downloaded debian/control from the testbed, so | ||
437 | 585 | # that we can avoid calling "apt-get build-dep" and thus | ||
438 | 586 | # introducing a second mechanism for installing build deps | ||
439 | 587 | pkg_control = adt_testbed.Path(testbed, | ||
440 | 588 | os.path.join(tmp, 'apt-control'), | ||
441 | 589 | os.path.join(result_pwd, 'debian/control'), False) | ||
442 | 590 | pkg_control.copyup() | ||
443 | 591 | dsc = pkg_control.host | ||
444 | 592 | |||
445 | 593 | with open(dsc, encoding='UTF-8') as f: | ||
446 | 594 | d = deb822.Deb822(sequence=f) | ||
447 | 595 | bd = d.get('Build-Depends', '') | ||
448 | 596 | bdi = d.get('Build-Depends-Indep', '') | ||
449 | 597 | bda = d.get('Build-Depends-Arch', '') | ||
450 | 598 | |||
451 | 599 | # determine build command and build-essential packages | 592 | # determine build command and build-essential packages |
453 | 600 | build_essential = ['build-essential'] | 593 | build_essential = ['build-essential:native'] |
454 | 601 | assert testbed.nproc | 594 | assert testbed.nproc |
455 | 602 | dpkg_buildpackage = 'DEB_BUILD_OPTIONS="parallel=%s $DEB_BUILD_OPTIONS" dpkg-buildpackage -us -uc -b' % ( | 595 | dpkg_buildpackage = 'DEB_BUILD_OPTIONS="parallel=%s $DEB_BUILD_OPTIONS" dpkg-buildpackage -us -uc -b' % ( |
456 | 603 | opts.build_parallel or testbed.nproc) | 596 | opts.build_parallel or testbed.nproc) |
457 | @@ -607,8 +600,47 @@ dpkg-source -x %(src)s_*.dsc src >/dev/null''' % {'src': arg} | |||
458 | 607 | if testbed.user or 'root-on-testbed' not in testbed.caps: | 600 | if testbed.user or 'root-on-testbed' not in testbed.caps: |
459 | 608 | build_essential += ['fakeroot'] | 601 | build_essential += ['fakeroot'] |
460 | 609 | 602 | ||
463 | 610 | testbed.satisfy_dependencies_string(bd + ', ' + bdi + ', ' + bda + ', ' + ', '.join(build_essential), arg, | 603 | if opts.architecture: |
464 | 611 | build_dep=True, shell_on_failure=opts.shell_fail) | 604 | dpkg_buildpackage += ' -a' + opts.architecture |
465 | 605 | build_essential += ['crossbuild-essential-%s:native' % opts.architecture] | ||
466 | 606 | # apt-get build-dep is the best option here, but we don't call | ||
467 | 607 | # it unconditionally because it doesn't take a path to a source | ||
468 | 608 | # package as an option in very old releases; so for compatibility | ||
469 | 609 | # only use it when we need its multiarch build-dep resolution | ||
470 | 610 | # support. | ||
471 | 611 | # This is supported in apt 1.1~exp2 and newer, which covers | ||
472 | 612 | # Debian oldstable (at time of writing) and Ubuntu 16.04 and | ||
473 | 613 | # newer, so once we can rely on this version of apt everywhere, | ||
474 | 614 | # the old build-dep resolver code should be deprecated. | ||
475 | 615 | all_build_deps = '' | ||
476 | 616 | testbed.satisfy_build_deps(result_pwd, | ||
477 | 617 | architecture=opts.architecture, | ||
478 | 618 | shell_on_failure=opts.shell_fail) | ||
479 | 619 | else: | ||
480 | 620 | if kind in ('apt-source', 'git-source'): | ||
481 | 621 | # we need to get the downloaded debian/control from the | ||
482 | 622 | # testbed, so that we can avoid calling "apt-get build-dep" | ||
483 | 623 | # and thus introducing a second mechanism for installing | ||
484 | 624 | # build deps | ||
485 | 625 | pkg_control = adt_testbed.Path(testbed, | ||
486 | 626 | os.path.join(tmp, 'apt-control'), | ||
487 | 627 | os.path.join(result_pwd, 'debian/control'), False) | ||
488 | 628 | pkg_control.copyup() | ||
489 | 629 | dsc = pkg_control.host | ||
490 | 630 | |||
491 | 631 | with open(dsc, encoding='UTF-8') as f: | ||
492 | 632 | d = deb822.Deb822(sequence=f) | ||
493 | 633 | bd = d.get('Build-Depends', '') | ||
494 | 634 | bdi = d.get('Build-Depends-Indep', '') | ||
495 | 635 | bda = d.get('Build-Depends-Arch', '') | ||
496 | 636 | |||
497 | 637 | all_build_deps = bd + ', ' + bdi + ', ' + bda + ', ' | ||
498 | 638 | |||
499 | 639 | testbed.satisfy_dependencies_string(all_build_deps + | ||
500 | 640 | ', '.join(build_essential), | ||
501 | 641 | arg, | ||
502 | 642 | build_dep=True, | ||
503 | 643 | shell_on_failure=opts.shell_fail) | ||
504 | 612 | 644 | ||
505 | 613 | # keep patches applied for tests | 645 | # keep patches applied for tests |
506 | 614 | source_rules_command([dpkg_buildpackage, 'dpkg-source --before-build .'], 'build', cwd=result_pwd) | 646 | source_rules_command([dpkg_buildpackage, 'dpkg-source --before-build .'], 'build', cwd=result_pwd) |
507 | @@ -733,7 +765,8 @@ def process_actions(): | |||
508 | 733 | (tests, skipped) = testdesc.parse_debian_source( | 765 | (tests, skipped) = testdesc.parse_debian_source( |
509 | 734 | tests_tree.host, testbed.caps, testbed.dpkg_arch, | 766 | tests_tree.host, testbed.caps, testbed.dpkg_arch, |
510 | 735 | control_path=control_override, | 767 | control_path=control_override, |
512 | 736 | auto_control=opts.auto_control) | 768 | auto_control=opts.auto_control, |
513 | 769 | cross_arch=opts.architecture) | ||
514 | 737 | except testdesc.InvalidControl as e: | 770 | except testdesc.InvalidControl as e: |
515 | 738 | adtlog.badpkg(str(e)) | 771 | adtlog.badpkg(str(e)) |
516 | 739 | 772 | ||
517 | diff --git a/runner/autopkgtest.1 b/runner/autopkgtest.1 | |||
518 | index ce4a1bf..abf181b 100644 | |||
519 | --- a/runner/autopkgtest.1 | |||
520 | +++ b/runner/autopkgtest.1 | |||
521 | @@ -136,6 +136,12 @@ autopkgtest --installed-click com.example.myclick -- [...] | |||
522 | 136 | .SH TEST OPTIONS | 136 | .SH TEST OPTIONS |
523 | 137 | 137 | ||
524 | 138 | .TP | 138 | .TP |
525 | 139 | .BR -a " | " --architecture ARCH | ||
526 | 140 | Run tests for the specified architecture, rather than for the host | ||
527 | 141 | architecture as defined by dpkg \-\-print-architecture. When building | ||
528 | 142 | packages from source, cross-build for the target architecture as well. | ||
529 | 143 | |||
530 | 144 | .TP | ||
531 | 139 | .BR -B " | " --no-built-binaries | 145 | .BR -B " | " --no-built-binaries |
532 | 140 | Binaries from unbuilt source packages (see above) | 146 | Binaries from unbuilt source packages (see above) |
533 | 141 | will not be built or ignored, and dependencies are satisfied with packages from | 147 | will not be built or ignored, and dependencies are satisfied with packages from |
The commits look good to me from reading them, except that one comment below. Not performed any real-world testing yet.