diff mbox

[PULL,04/16] Makefile: Rules for docker testing

Message ID 1464774324-886-5-git-send-email-famz@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Fam Zheng June 1, 2016, 9:45 a.m. UTC
This adds a group of make targets to run docker tests, all are available
in source tree without running ./configure.

The usage is shown with "make docker".

Besides the fixed ones, dynamic targets for building each image and
running each test in each image are generated automatically by make,
scanning $(SRC_PATH)/tests/docker/ files with specific patterns.

Alternative to manually list particular targets (docker-TEST@IMAGE)
set, you can control which tests/images to run by filtering variables,
TESTS= and IMAGES=, which are expressed in Makefile pattern syntax,
"foo% %bar ...". For example:

    $ make docker-test IMAGES="ubuntu fedora"

Unfortunately, it's impossible to propagate "-j $JOBS" into make in
containers, however since each combination is made a first class target
in the top Makefile, "make -j$N docker-test" still parallels the tests
coarsely.

Still, $J is made a magic variable to let all make invocations in
containers to use -j$J.

Instead of providing a live version of the source tree to the docker
container we snapshot it with git-archive. This ensures the tree is in a
pristine state for whatever operations the container is going to run on
them.

Uncommitted changes known to files known by the git index will be
included in the snapshot if there are any.

Signed-off-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 1464755128-32490-5-git-send-email-famz@redhat.com
---
 Makefile                      |   4 +-
 tests/docker/Makefile.include | 123 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)
 create mode 100644 tests/docker/Makefile.include

Comments

Eduardo Habkost June 2, 2016, 7:13 p.m. UTC | #1
On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
[...]
> +
> +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> +# Makes the definition constant after the first expansion
> +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> +
> +$(DOCKER_SRC_COPY):

This seems to be confusing make in travis-ci, and I don't
understand why:
https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
Fam Zheng June 3, 2016, 1:23 a.m. UTC | #2
On Thu, 06/02 16:13, Eduardo Habkost wrote:
> On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> [...]
> > +
> > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > +# Makes the definition constant after the first expansion
> > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > +
> > +$(DOCKER_SRC_COPY):
> 
> This seems to be confusing make in travis-ci, and I don't
> understand why:
> https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266

Indeed, but qemu.git master doesn't have that. Wondering why.

I would like to help, but could you fix the "unused variable ‘numvalue’" error
first? It's distracting too much when I try travis with your branch myself.

Fam
Eduardo Habkost June 3, 2016, 5:33 p.m. UTC | #3
On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > [...]
> > > +
> > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > +# Makes the definition constant after the first expansion
> > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > +
> > > +$(DOCKER_SRC_COPY):
> > 
> > This seems to be confusing make in travis-ci, and I don't
> > understand why:
> > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> 
> Indeed, but qemu.git master doesn't have that. Wondering why.
> 
> I would like to help, but could you fix the "unused variable ‘numvalue’" error
> first? It's distracting too much when I try travis with your branch myself.

Oops, sorry. Fixed.

I can't reproduce the error in my machine, BTW. I only saw it
happen on Travis.
Eduardo Habkost June 3, 2016, 6:26 p.m. UTC | #4
On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > [...]
> > > +
> > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > +# Makes the definition constant after the first expansion
> > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > +
> > > +$(DOCKER_SRC_COPY):
> > 
> > This seems to be confusing make in travis-ci, and I don't
> > understand why:
> > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> 
> Indeed, but qemu.git master doesn't have that. Wondering why.

I have pushed master to a temporary branch on my tree, and the
issue persists:

https://travis-ci.org/ehabkost/qemu/builds/135090745
Fam Zheng June 6, 2016, 6:58 a.m. UTC | #5
On Fri, 06/03 15:26, Eduardo Habkost wrote:
> On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> > On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > > [...]
> > > > +
> > > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > > +# Makes the definition constant after the first expansion
> > > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > > +
> > > > +$(DOCKER_SRC_COPY):
> > > 
> > > This seems to be confusing make in travis-ci, and I don't
> > > understand why:
> > > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> > 
> > Indeed, but qemu.git master doesn't have that. Wondering why.
> 
> I have pushed master to a temporary branch on my tree, and the
> issue persists:
> 
> https://travis-ci.org/ehabkost/qemu/builds/135090745

I pushed the same commit 6b3532b20b787cbd697a68b383232f5c3b39bd1e to my github
repo and the triggered travis build doesn't error at the same location.  I have
no idea what is happening there.  Wild guesses: do you have any non-default
travis per-repo or per-branch settings? Have you tried deleting all caches at:

https://docs.travis-ci.com/user/caching/

?

Fam
Eduardo Habkost June 6, 2016, 11:47 a.m. UTC | #6
On Mon, Jun 06, 2016 at 02:58:24PM +0800, Fam Zheng wrote:
> On Fri, 06/03 15:26, Eduardo Habkost wrote:
> > On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> > > On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > > > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > > > [...]
> > > > > +
> > > > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > > > +# Makes the definition constant after the first expansion
> > > > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > > > +
> > > > > +$(DOCKER_SRC_COPY):
> > > > 
> > > > This seems to be confusing make in travis-ci, and I don't
> > > > understand why:
> > > > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> > > 
> > > Indeed, but qemu.git master doesn't have that. Wondering why.
> > 
> > I have pushed master to a temporary branch on my tree, and the
> > issue persists:
> > 
> > https://travis-ci.org/ehabkost/qemu/builds/135090745
> 
> I pushed the same commit 6b3532b20b787cbd697a68b383232f5c3b39bd1e to my github
> repo and the triggered travis build doesn't error at the same location.  I have
> no idea what is happening there.  Wild guesses: do you have any non-default
> travis per-repo or per-branch settings? Have you tried deleting all caches at:
> 
> https://docs.travis-ci.com/user/caching/
> 
> ?

No non-default settings, as far as I can see. I have:
* Build only if .travis.yml is present: OFF
* Limit concurrent jobs: OFF
* Build pushes: ON
* Build pull requests: ON
* Environment variables: none

No .travis.yml changes on any of my branches.

I have just deleted all caches and I will send a new push. I will
let you know the results soon.
Eduardo Habkost June 6, 2016, 11:54 a.m. UTC | #7
On Mon, Jun 06, 2016 at 08:47:24AM -0300, Eduardo Habkost wrote:
> On Mon, Jun 06, 2016 at 02:58:24PM +0800, Fam Zheng wrote:
> > On Fri, 06/03 15:26, Eduardo Habkost wrote:
> > > On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> > > > On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > > > > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > > > > [...]
> > > > > > +
> > > > > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > > > > +# Makes the definition constant after the first expansion
> > > > > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > > > > +
> > > > > > +$(DOCKER_SRC_COPY):
> > > > > 
> > > > > This seems to be confusing make in travis-ci, and I don't
> > > > > understand why:
> > > > > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> > > > 
> > > > Indeed, but qemu.git master doesn't have that. Wondering why.
> > > 
> > > I have pushed master to a temporary branch on my tree, and the
> > > issue persists:
> > > 
> > > https://travis-ci.org/ehabkost/qemu/builds/135090745
> > 
> > I pushed the same commit 6b3532b20b787cbd697a68b383232f5c3b39bd1e to my github
> > repo and the triggered travis build doesn't error at the same location.  I have
> > no idea what is happening there.  Wild guesses: do you have any non-default
> > travis per-repo or per-branch settings? Have you tried deleting all caches at:
> > 
> > https://docs.travis-ci.com/user/caching/
> > 
> > ?
> 
> No non-default settings, as far as I can see. I have:
> * Build only if .travis.yml is present: OFF
> * Limit concurrent jobs: OFF
> * Build pushes: ON
> * Build pull requests: ON
> * Environment variables: none
> 
> No .travis.yml changes on any of my branches.
> 
> I have just deleted all caches and I will send a new push. I will
> let you know the results soon.

Same: https://travis-ci.org/ehabkost/qemu/jobs/135569978

I will make some changes to test if this really has anything to
do with the Makefile code, or if there's something wrong on
Travis side.
Eduardo Habkost June 6, 2016, 12:50 p.m. UTC | #8
On Mon, Jun 06, 2016 at 08:54:23AM -0300, Eduardo Habkost wrote:
> On Mon, Jun 06, 2016 at 08:47:24AM -0300, Eduardo Habkost wrote:
> > On Mon, Jun 06, 2016 at 02:58:24PM +0800, Fam Zheng wrote:
> > > On Fri, 06/03 15:26, Eduardo Habkost wrote:
> > > > On Fri, Jun 03, 2016 at 09:23:28AM +0800, Fam Zheng wrote:
> > > > > On Thu, 06/02 16:13, Eduardo Habkost wrote:
> > > > > > On Wed, Jun 01, 2016 at 05:45:12PM +0800, Fam Zheng wrote:
> > > > > > [...]
> > > > > > > +
> > > > > > > +CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
> > > > > > > +# Makes the definition constant after the first expansion
> > > > > > > +DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> > > > > > > +
> > > > > > > +$(DOCKER_SRC_COPY):
> > > > > > 
> > > > > > This seems to be confusing make in travis-ci, and I don't
> > > > > > understand why:
> > > > > > https://travis-ci.org/ehabkost/qemu/jobs/134820251#L1266
> > > > > 
> > > > > Indeed, but qemu.git master doesn't have that. Wondering why.
> > > > 
> > > > I have pushed master to a temporary branch on my tree, and the
> > > > issue persists:
> > > > 
> > > > https://travis-ci.org/ehabkost/qemu/builds/135090745
> > > 
> > > I pushed the same commit 6b3532b20b787cbd697a68b383232f5c3b39bd1e to my github
> > > repo and the triggered travis build doesn't error at the same location.  I have
> > > no idea what is happening there.  Wild guesses: do you have any non-default
> > > travis per-repo or per-branch settings? Have you tried deleting all caches at:
> > > 
> > > https://docs.travis-ci.com/user/caching/
> > > 
> > > ?
> > 
> > No non-default settings, as far as I can see. I have:
> > * Build only if .travis.yml is present: OFF
> > * Limit concurrent jobs: OFF
> > * Build pushes: ON
> > * Build pull requests: ON
> > * Environment variables: none
> > 
> > No .travis.yml changes on any of my branches.
> > 
> > I have just deleted all caches and I will send a new push. I will
> > let you know the results soon.
> 
> Same: https://travis-ci.org/ehabkost/qemu/jobs/135569978
> 
> I will make some changes to test if this really has anything to
> do with the Makefile code, or if there's something wrong on
> Travis side.

I have changed the code to:

  CUR_TIME := $(shell date +%Y.$$$$)
  
  $(shell echo DEBUG: cur_time: $(CUR_TIME) >&2)
  
  # Makes the definition constant after the first expansion
  DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
  
  $(shell echo DEBUG: src_copy: $(DOCKER_SRC_COPY) >&2)
  
  $(DOCKER_SRC_COPY):
  	@mkdir $@
  	$(call make-archive-maybe, $(SRC_PATH), $@/qemu.tgz)

and I still get a failure: https://travis-ci.org/ehabkost/qemu/jobs/135574871

  $ make -j3 && ${TEST_CMD}
  DEBUG: cur_time: 2016.13278
  /home/travis/build/ehabkost/qemu/tests/docker/Makefile.include:37: *** unterminated variable reference.  Stop.

I am very confused by that code: using "=" to make a variable
reference itself is not allowed, but in this case no error is
reported because of the $(eval) trick. I have no idea what will
happen the second time the variable is expanded. Isn't $(eval)
going to be expanded again?

Why did you use this trick instead of just relying on ":=" to
expand the shell command only once?
Paolo Bonzini June 6, 2016, 2:01 p.m. UTC | #9
On 06/06/2016 14:50, Eduardo Habkost wrote:
>   # Makes the definition constant after the first expansion
>   DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
>   
>   $(shell echo DEBUG: src_copy: $(DOCKER_SRC_COPY) >&2)
>   
>   $(DOCKER_SRC_COPY):
>   	@mkdir $@
>   	$(call make-archive-maybe, $(SRC_PATH), $@/qemu.tgz)
> 
> and I still get a failure: https://travis-ci.org/ehabkost/qemu/jobs/135574871
> 
>   $ make -j3 && ${TEST_CMD}
>   DEBUG: cur_time: 2016.13278
>   /home/travis/build/ehabkost/qemu/tests/docker/Makefile.include:37: *** unterminated variable reference.  Stop.
> 
> I am very confused by that code: using "=" to make a variable
> reference itself is not allowed, but in this case no error is
> reported because of the $(eval) trick. I have no idea what will
> happen the second time the variable is expanded. Isn't $(eval)
> going to be expanded again?

No, the second time you'll get the expansion of

DOCKER_SRC_COPY := docker-src.$(CUR_TIME)

> Why did you use this trick instead of just relying on ":=" to
> expand the shell command only once?

I think initially the $(mkdir) was in DOCKER_SRC_COPY too.  Also, too
many $(shell) end up slowing down make.

Paolo
Eduardo Habkost June 6, 2016, 2:15 p.m. UTC | #10
On Mon, Jun 06, 2016 at 04:01:53PM +0200, Paolo Bonzini wrote:
> 
> 
> On 06/06/2016 14:50, Eduardo Habkost wrote:
> >   # Makes the definition constant after the first expansion
> >   DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
> >   
> >   $(shell echo DEBUG: src_copy: $(DOCKER_SRC_COPY) >&2)
> >   
> >   $(DOCKER_SRC_COPY):
> >   	@mkdir $@
> >   	$(call make-archive-maybe, $(SRC_PATH), $@/qemu.tgz)
> > 
> > and I still get a failure: https://travis-ci.org/ehabkost/qemu/jobs/135574871
> > 
> >   $ make -j3 && ${TEST_CMD}
> >   DEBUG: cur_time: 2016.13278
> >   /home/travis/build/ehabkost/qemu/tests/docker/Makefile.include:37: *** unterminated variable reference.  Stop.
> > 
> > I am very confused by that code: using "=" to make a variable
> > reference itself is not allowed, but in this case no error is
> > reported because of the $(eval) trick. I have no idea what will
> > happen the second time the variable is expanded. Isn't $(eval)
> > going to be expanded again?
> 
> No, the second time you'll get the expansion of
> 
> DOCKER_SRC_COPY := docker-src.$(CUR_TIME)

And why it isn't enough to simply write the above line in the
Makefile without any eval trick?

> 
> > Why did you use this trick instead of just relying on ":=" to
> > expand the shell command only once?
> 
> I think initially the $(mkdir) was in DOCKER_SRC_COPY too.  Also, too
> many $(shell) end up slowing down make.

And how exactly does the eval trick helps avoiding slowing down
make, that wouldn't happen if using just:
  DOCKER_SRC_COPY = docker-src.$(CUR_TIME)
?
Paolo Bonzini June 6, 2016, 2:32 p.m. UTC | #11
On 06/06/2016 16:15, Eduardo Habkost wrote:
>> > No, the second time you'll get the expansion of
>> > 
>> > DOCKER_SRC_COPY := docker-src.$(CUR_TIME)
> And why it isn't enough to simply write the above line in the
> Makefile without any eval trick?

Because 1) initially the idea was to delay the $(mkdir) too 2) Too many
$(shell) end up slowing down make.

>>> > > Why did you use this trick instead of just relying on ":=" to
>>> > > expand the shell command only once?
>> > 
>> > I think initially the $(mkdir) was in DOCKER_SRC_COPY too.  Also, too
>> > many $(shell) end up slowing down make.
> And how exactly does the eval trick helps avoiding slowing down
> make, that wouldn't happen if using just:
>   DOCKER_SRC_COPY = docker-src.$(CUR_TIME)
> ?

If you define CUR_TIME with "=", the DOCKER_SRC_COPY value changes every
second.  If you define CUR_TIME with ":=", again you have a useless
$(shell).

In this particular case I agree that it's probably premature optimization.

Thanks,

Paolo
Eduardo Habkost June 6, 2016, 3:02 p.m. UTC | #12
On Mon, Jun 06, 2016 at 04:32:50PM +0200, Paolo Bonzini wrote:
> On 06/06/2016 16:15, Eduardo Habkost wrote:
> >> > No, the second time you'll get the expansion of
> >> > 
> >> > DOCKER_SRC_COPY := docker-src.$(CUR_TIME)
> > And why it isn't enough to simply write the above line in the
> > Makefile without any eval trick?
> 
> Because 1) initially the idea was to delay the $(mkdir) too 2) Too many
> $(shell) end up slowing down make.

Now I see: you wanted to delay the shell command, but evaluate it
only once.

But I don't see why it makes sense here, because the variable is
being expanded immediately after it is defined (in the
"$(DOCKER_SRC_COPY):" line), and CUR_TIME is already defined
using ":=".

> 
> >>> > > Why did you use this trick instead of just relying on ":=" to
> >>> > > expand the shell command only once?
> >> > 
> >> > I think initially the $(mkdir) was in DOCKER_SRC_COPY too.  Also, too
> >> > many $(shell) end up slowing down make.
> > And how exactly does the eval trick helps avoiding slowing down
> > make, that wouldn't happen if using just:
> >   DOCKER_SRC_COPY = docker-src.$(CUR_TIME)
> > ?

Oops, I mean:
  DOCKER_SRC_COPY := docker-src.$(CUR_TIME)

> 
> If you define CUR_TIME with "=", the DOCKER_SRC_COPY value changes every
> second.  If you define CUR_TIME with ":=", again you have a useless
> $(shell).

Why a useless shell?

> 
> In this particular case I agree that it's probably premature optimization.

In this particular case it doesn't seem to be an optimization at
all.
Paolo Bonzini June 6, 2016, 3:08 p.m. UTC | #13
On 06/06/2016 17:02, Eduardo Habkost wrote:
> On Mon, Jun 06, 2016 at 04:32:50PM +0200, Paolo Bonzini wrote:
>> On 06/06/2016 16:15, Eduardo Habkost wrote:
>>>>> No, the second time you'll get the expansion of
>>>>>
>>>>> DOCKER_SRC_COPY := docker-src.$(CUR_TIME)
>>> And why it isn't enough to simply write the above line in the
>>> Makefile without any eval trick?
>>
>> Because 1) initially the idea was to delay the $(mkdir) too 2) Too many
>> $(shell) end up slowing down make.
> 
> Now I see: you wanted to delay the shell command, but evaluate it
> only once.
> 
> But I don't see why it makes sense here, because the variable is
> being expanded immediately after it is defined (in the
> "$(DOCKER_SRC_COPY):" line), and CUR_TIME is already defined
> using ":=".

Well, when I proposed the eval trick CUR_TIME was defined with "=". :)

I agree that right now it's all unnecessary.

Paolo
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 1c2a7bd..b8563db 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@  BUILD_DIR=$(CURDIR)
 # Before including a proper config-host.mak, assume we are in the source tree
 SRC_PATH=.
 
-UNCHECKED_GOALS := %clean TAGS cscope ctags
+UNCHECKED_GOALS := %clean TAGS cscope ctags docker docker-%
 
 # All following code might depend on configuration variables
 ifneq ($(wildcard config-host.mak),)
@@ -644,3 +644,5 @@  endif
 # Include automatically generated dependency files
 # Dependencies in Makefile.objs files come from our recursive subdir rules
 -include $(wildcard *.d tests/*.d)
+
+include $(SRC_PATH)/tests/docker/Makefile.include
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
new file mode 100644
index 0000000..ef8bcae
--- /dev/null
+++ b/tests/docker/Makefile.include
@@ -0,0 +1,123 @@ 
+# Makefile for Docker tests
+
+.PHONY: docker docker-test docker-clean docker-image docker-qemu-src
+
+DOCKER_SUFFIX := .docker
+DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles
+DOCKER_IMAGES := $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker)))
+DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES))
+# Use a global constant ccache directory to speed up repetitive builds
+DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache
+
+DOCKER_TESTS := $(notdir $(shell \
+	find $(SRC_PATH)/tests/docker/ -name 'test-*' -type f))
+
+DOCKER_TOOLS := travis
+
+TESTS ?= %
+IMAGES ?= %
+
+# Make archive from git repo $1 to tar.gz $2
+make-archive-maybe = $(if $(wildcard $1/*), \
+	$(call quiet-command, \
+		(cd $1; if git diff-index --quiet HEAD -- &>/dev/null; then \
+			git archive -1 HEAD --format=tar.gz -o $2; \
+		else \
+			git archive -1 $$(git stash create) --format=tar.gz -o $2; \
+		fi), \
+		"  ARCHIVE $(notdir $2)"))
+
+CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
+# Makes the definition constant after the first expansion
+DOCKER_SRC_COPY = $(eval DOCKER_SRC_COPY := docker-src.$(CUR_TIME))$(DOCKER_SRC_COPY)
+
+$(DOCKER_SRC_COPY):
+	@mkdir $@
+	$(call make-archive-maybe, $(SRC_PATH), $@/qemu.tgz)
+	$(call make-archive-maybe, $(SRC_PATH)/dtc, $@/dtc.tgz)
+	$(call make-archive-maybe, $(SRC_PATH)/pixman, $@/pixman.tgz)
+	$(call quiet-command, cp $(SRC_PATH)/tests/docker/run $@/run, \
+		"  COPY RUNNER")
+
+docker-qemu-src: $(DOCKER_SRC_COPY)
+
+docker-image: ${DOCKER_TARGETS}
+
+# General rule for building docker images
+docker-image-%: $(DOCKER_FILES_DIR)/%.docker
+	$(call quiet-command,\
+		$(SRC_PATH)/tests/docker/docker.py build qemu:$* $< \
+		$(if $V,,--quiet) $(if $(NOCACHE),--no-cache),\
+		"  BUILD $*")
+
+# Expand all the pre-requistes for each docker image and test combination
+$(foreach i,$(DOCKER_IMAGES), \
+	$(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \
+		$(eval .PHONY: docker-$t@$i) \
+		$(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \
+	) \
+	$(foreach t,$(DOCKER_TESTS), \
+		$(eval docker-test: docker-$t@$i) \
+	) \
+)
+
+docker:
+	@echo 'Build QEMU and run tests inside Docker containers'
+	@echo
+	@echo 'Available targets:'
+	@echo
+	@echo '    docker:              Print this help.'
+	@echo '    docker-test:         Run all image/test combinations.'
+	@echo '    docker-clean:        Kill and remove residual docker testing containers.'
+	@echo '    docker-TEST@IMAGE:   Run "TEST" in container "IMAGE".'
+	@echo '                         Note: "TEST" is one of the listed test name,'
+	@echo '                         or a script name under $$QEMU_SRC/tests/docker/;'
+	@echo '                         "IMAGE" is one of the listed container name."'
+	@echo '    docker-image:        Build all images.'
+	@echo '    docker-image-IMAGE:  Build image "IMAGE".'
+	@echo
+	@echo 'Available container images:'
+	@echo '    $(DOCKER_IMAGES)'
+	@echo
+	@echo 'Available tests:'
+	@echo '    $(DOCKER_TESTS)'
+	@echo
+	@echo 'Available tools:'
+	@echo '    $(DOCKER_TOOLS)'
+	@echo
+	@echo 'Special variables:'
+	@echo '    TARGET_LIST=a,b,c    Override target list in builds.'
+	@echo '    IMAGES="a b c ..":   Filters which images to build or run.'
+	@echo '    TESTS="x y z .."     Filters which tests to run (for docker-test).'
+	@echo '    J=[0..9]*            Overrides the -jN parameter for make commands'
+	@echo '                         (default is 1)'
+	@echo '    DEBUG=1              Stop and drop to shell in the created container'
+	@echo '                         before running the command.'
+	@echo '    NOCACHE=1            Ignore cache when build images.'
+
+docker-run-%: CMD = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\1/')
+docker-run-%: IMAGE = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\2/')
+docker-run-%: docker-qemu-src
+	@mkdir -p "$(DOCKER_CCACHE_DIR)"
+	@if test -z "$(IMAGE)" || test -z "$(CMD)"; \
+		then echo "Invalid target"; exit 1; \
+	fi
+	$(if $(filter $(TESTS),$(CMD)),$(if $(filter $(IMAGES),$(IMAGE)), \
+		$(call quiet-command,\
+			$(SRC_PATH)/tests/docker/docker.py run $(if $V,,--rm) \
+				-t \
+				$(if $(DEBUG),-i,--net=none) \
+				-e TARGET_LIST=$(TARGET_LIST) \
+				-e V=$V -e J=$J -e DEBUG=$(DEBUG)\
+				-e CCACHE_DIR=/var/tmp/ccache \
+				-v $$(realpath $(DOCKER_SRC_COPY)):/var/tmp/qemu:z$(COMMA)ro \
+				-v $(DOCKER_CCACHE_DIR):/var/tmp/ccache:z \
+				-w /var/tmp/qemu \
+				qemu:$(IMAGE) \
+				$(if $V,/bin/bash -x ,) \
+				./run \
+				$(CMD); \
+			, "  RUN $(CMD) in $(IMAGE)")))
+
+docker-clean:
+	$(call quiet-command, $(SRC_PATH)/tests/docker/docker.py clean)