I playing with Docker and make utility and try to write rule which rebuilds docker image only on Dockerfile change.
My project structure looks like:
tree .
.
├── Dockerfile
├── Makefile
└── project
└── 1.js
My Dockerfile is pretty simple:
FROM ubuntu
RUN apt-get update
RUN apt-get install -y curl
RUN curl -sL https://deb.nodesource.com/setup | sudo bash -
RUN apt-get update
RUN apt-get install -y build-essential nodejs
VOLUME ["/project"]
ENTRYPOINT ["cat"]
CMD ["project/1.js"]
It just creates simple ubuntu image with nodejs installation and run a script from shared directory.
Now I want to run this image from Makefile. When I change a Dockerfile I want to rebuild the image. Makefile looks like:
default: run
run: build
docker run -v $(CURDIR)/project:/project app-server
build: Dockerfile
docker build -t app-server .
Now when I execute sudo make
command it rebuild an image every time.
How can I force make to execute build task only when Dockerfile changed?
Your "targets"
default
run
andbuild
are "phony" targets. That is an abstract concept, not a real file. Such phony targets, should not have a recipe (because you can't make them). They should instead depend on real files, or perhaps other phony targets, and so on, but everything must eventually depend on real files only.Your phony targets should be marked as such
The real targets, on the other hand, should have a recipe - the recipe makes that target.
So, first depend your phony targets, without a recipe, on a real target(s).
Then have real targets have recipes.
I have posted some guidelines at makefile enforce library dependency ordering
When you write:
in a makefile make expects that that recipe will create a file by the name of
run
. make will then check that file's timestamp against the timestamp of its prerequisite files to determine if the recipe needs to be run the next time.Similarly with the
build
target you have in your makefile.Neither of those recipes create files with the name of the target however. This means that make cannot use the timestamp of that file to determine whether it needs to re-run the recipe. As such make has to assume that it needs to re-run the recipe (because assuming otherwise would mean the rule would never run).
If you run
make -rRd
you will see what make thinks is going on and you should see indication of what I've just said.The solution to your problem, therefore, is to create stamp files in each of those targets.
Simply adding
touch $@
(optionally prefixed with@
to silence the default make echoing of commands it runs) to each of those targets should be enough to get this to work for you.That being said it might make sense to put
sudo
on each of the recipe lines that need it instead of runningmake
withsudo
if you don't want the stamp files to be owned as root as well.For the record this is discussed in the GNU Make Manual as section
4.8 Empty Target Files to Record Events
.