@samitouri / QOSamiQemu / commits / c4ce04cfb7

tests/docker: ensure container command is probed at most once

The '--engine' arg accepts either 'podman' or 'docker', which is not sufficiently granular to map directly to a command. This means that docker.py still has to then probe the exact command to use. Meanwhile the 'probe' command prints out the full command to use but this cannot be passed back to docker.py to avoid probing again, so the caching is only useful in the few case where we run a container directly bypassing docker.py. Address this by replacing --engine with --command for docker.py. This in turn requires the --container-engine configure arg to be replaced with --container-command. With these changes the container command is probed at most once during configure and never again, while running in an unconfigured tree will still probe on demand. Tested-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260716102515.1503404-1-berrange@redhat.com Signed-off-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>

Daniel P. Berrangé committed Jul 16, 2026 at 11:25 UTC c4ce04cfb7460d46c0262d437a7f35bb5c5fc449
3 files changed +40 -65
configure
+12 -13
@@ -172,7 +172,7 @@ fi
172 # some defaults, based on the host environment
173
174 # default parameters
175 -container_engine="auto"
175 +container_command=""
176 cpu=""
177 cross_compile="no"
178 cross_prefix=""
@@ -734,7 +734,7 @@ for opt do
734 ;;
735 --disable-containers) use_containers="no"
736 ;;
737 - --container-engine=*) container_engine="$optarg"
737 + --container-command=*) container_command="$optarg"
738 ;;
739 --rust-target-triple=*) rust_target_triple="$optarg"
740 ;;
@@ -869,7 +869,7 @@ Advanced options (experts only):
869 --enable-debug enable common debug build options
870 --cpu=CPU Build for host CPU [$cpu]
871 --disable-containers don't use containers for cross-building
872 - --container-engine=TYPE which container engine to use [$container_engine]
872 + --container-command=CMD which container command to use [autodetect]
873 --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin]
874 --wasm64-32bit-address-limit Restrict wasm64 address space to 32-bit (default
875 is to use the whole 64-bit range).
@@ -1291,12 +1291,12 @@ fi
1291 ##########################################
1292 # functions to probe cross compilers
1293
1294 -runc="no"
1295 -if test $use_containers = "yes" && (has "docker" || has "podman"); then
1296 - runc=$($python "$source_path"/tests/docker/docker.py --engine "$container_engine" probe)
1297 - if test "$runc" != "no"; then
1298 - docker_py="$python $source_path/tests/docker/docker.py --engine $container_engine"
1299 - fi
1294 +if test "$container_command" = ""; then
1295 + container_command=$($python "$source_path"/tests/docker/docker.py probe)
1296 + test "$container_command" = "no" && container_command=""
1297 +fi
1298 +if test $use_containers = "yes" && test "$container_command" != ""; then
1299 + docker_py="$python $source_path/tests/docker/docker.py --command $container_command"
1300 fi
1301
1302 # cross compilers defaults, can be overridden with --cross-cc-ARCH
@@ -1415,7 +1415,7 @@ probe_target_compiler() {
1415 esac
1416
1417 for host in $container_hosts; do
1418 - test "$runc" != no || continue
1418 + test "$container_command" != "" || continue
1419 test "$host" = "$cpu" || continue
1420 case $target_arch in
1421 # debian-all-test-cross architectures
@@ -1736,9 +1736,8 @@ echo all: >> $config_host_mak
1736 echo "SRC_PATH=$source_path" >> $config_host_mak
1737 echo "TARGET_DIRS=$target_list" >> $config_host_mak
1738 echo "GDB=$gdb_bin" >> $config_host_mak
1739 -if test "$runc" != no; then
1740 - echo "RUNC=$runc" >> $config_host_mak
1741 - echo "CONTAINER_ENGINE=$container_engine" >> $config_host_mak
1739 +if test "$container_command" != ""; then
1740 + echo "CONTAINER_COMMAND=$container_command" >> $config_host_mak
1741 fi
1742 echo "SUBDIRS=$subdirs" >> $config_host_mak
1743 if test "$rust" != disabled; then
tests/docker/Makefile.include
+6 -7
@@ -16,9 +16,8 @@ DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu
16 endif
17 DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY))
18
19 -CONTAINER_ENGINE = auto
20 -DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(CONTAINER_ENGINE)
21 -RUNC ?= $(shell $(DOCKER_SCRIPT) probe)
19 +CONTAINER_COMMAND ?= $(shell $(SRC_PATH)/tests/docker/docker.py probe)
20 +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --command "$(CONTAINER_COMMAND)"
21
22 CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
23 DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME)
@@ -41,7 +40,7 @@ docker-qemu-src: $(DOCKER_SRC_COPY)
40 # General rule for building docker images.
41 docker-image-%: $(DOCKER_FILES_DIR)/%.docker
42 $(call quiet-command, \
44 - DOCKER_BUILDKIT=1 $(RUNC) build \
43 + DOCKER_BUILDKIT=1 $(CONTAINER_COMMAND) build \
44 $(if $(DOCKER_V),,--quiet) \
45 $(if $(NOCACHE),--no-cache, \
46 $(if $(DOCKER_REGISTRY),--cache-from $(DOCKER_REGISTRY)/qemu/$*)) \
@@ -152,7 +151,7 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \
151 )
152
153 docker:
155 - @echo 'Build QEMU and run tests inside $(RUNC) containers'
154 + @echo 'Build QEMU and run tests inside $(CONTAINER_COMMAND) containers'
155 @echo
156 @echo 'Available targets:'
157 @echo
@@ -219,10 +218,10 @@ docker-run: docker-qemu-src
218 $(IMAGE) --executable $(EXECUTABLE), \
219 " COPYING $(EXECUTABLE) to $(IMAGE)"))
220 $(call quiet-command, \
222 - $(RUNC) run \
221 + $(CONTAINER_COMMAND) run \
222 --rm \
223 $(if $(NOUSER),, \
225 - $(if $(filter docker,$(RUNC)), \
224 + $(if $(filter docker,$(CONTAINER_COMMAND)), \
225 -u $(UID), \
226 --userns keep-id \
227 ) \
tests/docker/docker.py
+22 -45
@@ -35,27 +35,6 @@ FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy']
35
36 DEVNULL = open(os.devnull, 'wb')
37
38 -class EngineEnum(enum.IntEnum):
39 - AUTO = 1
40 - DOCKER = 2
41 - PODMAN = 3
42 -
43 - def __str__(self):
44 - return self.name.lower()
45 -
46 - def __repr__(self):
47 - return str(self)
48 -
49 - @staticmethod
50 - def argparse(s):
51 - try:
52 - return EngineEnum[s.upper()]
53 - except KeyError:
54 - return s
55 -
56 -
57 -USE_ENGINE = EngineEnum.AUTO
58 -
38 def _bytes_checksum(bytes):
39 """Calculate a digest string unique to the text content"""
40 return hashlib.sha1(bytes).hexdigest()
@@ -73,12 +52,11 @@ def _file_checksum(filename):
52
53 def _guess_engine_command():
54 """ Guess a working engine command or raise exception if not found"""
76 - commands = []
77 -
78 - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.PODMAN]:
79 - commands += [["podman"], ["podman-remote"], ["podman", "--remote"]]
80 - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.DOCKER]:
81 - commands += [["docker"], ["sudo", "-n", "docker"]]
55 + commands = [["podman"],
56 + ["podman-remote"],
57 + ["podman", "--remote"],
58 + ["docker"],
59 + ["sudo", "-n", "docker"]]
60 for cmd in commands:
61 try:
62 # 'version' is not sufficient to prove a working binary
@@ -222,8 +200,11 @@ def _dockerfile_verify_flat(df):
200
201 class Docker(object):
202 """ Running Docker commands """
225 - def __init__(self):
226 - self._command = _guess_engine_command()
203 + def __init__(self, commandstr=None):
204 + if commandstr is None:
205 + self._command = _guess_engine_command()
206 + else:
207 + self._command = commandstr.split(" ")
208
209 if ("docker" in self._command and
210 "TRAVIS" not in os.environ and
@@ -411,8 +392,8 @@ class RunCommand(SubCommand):
392 help="Run container using the current user's uid")
393
394 def run(self, args, argv):
414 - return Docker().run(argv, args.keep, quiet=args.quiet,
415 - as_user=args.run_as_current_user)
395 + return Docker(args.command).run(argv, args.keep, quiet=args.quiet,
396 + as_user=args.run_as_current_user)
397
398
399 class BuildCommand(SubCommand):
@@ -445,7 +426,7 @@ class BuildCommand(SubCommand):
426 dockerfile = _read_dockerfile(args.dockerfile)
427 tag = args.tag
428
448 - dkr = Docker()
429 + dkr = Docker(args.command)
430 if "--no-cache" not in argv and \
431 dkr.image_matches_dockerfile(tag, dockerfile):
432 if not args.quiet:
@@ -512,7 +493,7 @@ class FetchCommand(SubCommand):
493 help="Docker registry")
494
495 def run(self, args, argv):
515 - dkr = Docker()
496 + dkr = Docker(args.command)
497 dkr.command(cmd="pull", quiet=args.quiet,
498 argv=["%s/%s" % (args.registry, args.tag)])
499 dkr.command(cmd="tag", quiet=args.quiet,
@@ -590,7 +571,7 @@ class UpdateCommand(SubCommand):
571 tmp.seek(0)
572
573 # Run the build with our tarball context
593 - dkr = Docker()
574 + dkr = Docker(args.command)
575 dkr.update_image(args.tag, tmp, quiet=args.quiet)
576
577 return 0
@@ -601,7 +582,7 @@ class CleanCommand(SubCommand):
582 name = "clean"
583
584 def run(self, args, argv):
604 - Docker().clean()
585 + Docker(args.command).clean()
586 return 0
587
588
@@ -610,7 +591,7 @@ class ImagesCommand(SubCommand):
591 name = "images"
592
593 def run(self, args, argv):
613 - return Docker().command("images", argv, args.quiet)
594 + return Docker(args.command).command("images", argv, args.quiet)
595
596
597 class ProbeCommand(SubCommand):
@@ -619,7 +600,7 @@ class ProbeCommand(SubCommand):
600
601 def run(self, args, argv):
602 try:
622 - docker = Docker()
603 + docker = Docker(args.command)
604 print(" ".join(docker._command))
605 except Exception:
606 print("no")
@@ -651,18 +632,16 @@ class CcCommand(SubCommand):
632 cmd += ["-v", "%s:%s:ro,z" % (p, p)]
633 cmd += [args.image, args.cc]
634 cmd += argv
654 - return Docker().run(cmd, False, quiet=args.quiet,
655 - as_user=True)
635 + return Docker(args.command).run(cmd, False, quiet=args.quiet,
636 + as_user=True)
637
638
639 def main():
659 - global USE_ENGINE
660 -
640 parser = argparse.ArgumentParser(description="A Docker helper",
641 usage="%s <subcommand> ..." %
642 os.path.basename(sys.argv[0]))
664 - parser.add_argument("--engine", type=EngineEnum.argparse, choices=list(EngineEnum),
665 - help="specify which container engine to use")
643 + parser.add_argument("--command",
644 + help="specify which container engine command to use")
645 subparsers = parser.add_subparsers(title="subcommands", help=None)
646 for cls in SubCommand.__subclasses__():
647 cmd = cls()
@@ -671,8 +650,6 @@ def main():
650 cmd.args(subp)
651 subp.set_defaults(cmdobj=cmd)
652 args, argv = parser.parse_known_args()
674 - if args.engine:
675 - USE_ENGINE = args.engine
653 return args.cmdobj.run(args, argv)
654
655