Javier Lopez Tomas
Javier Lopez Tomas

Reputation: 2352

Use .PHONY targets as variables in Makefile

I have a Makefile with several commands that are almost the same except for the parameter SCRIPT, which takes the same value of the target:

.PHONY: create run

create:
    docker build -t yoyo --build-arg SCRIPT=create . \
    && docker run -v ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo

run:
    docker build -t yoyo --build-arg SCRIPT=run . \
    && docker run -v ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo

What I would like to do is to convert the target that I pass through the command line into a variable so that I can have my Makefile like:

.PHONY: create run

$target:
    docker build -t yoyo --build-arg SCRIPT=$target . \
    && docker run -v ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo

Is this possible somehow? If not, is there any similar way to simplify my Makefile? Thanks

Upvotes: 0

Views: 873

Answers (3)

David Maze
David Maze

Reputation: 159171

The first thing that jumps out at me is that you're rebuilding your image just to run a different script. You shouldn't need to do that. You can pass an additional command to docker run after the image name, and this will replace the CMD shown in the Dockerfile. I'll assume it's straightforward to remove the ARG and update your CMD and ENTRYPOINT accordingly.

That means you need one rule to build the image:

DOCKER_TAG := latest
DOCKER_IMAGE := yoyo:$(DOCKER_TAG)
# This rule should depend on any generated files that get COPYed in
.docker-build:
        docker build -t $(DOCKER_IMAGE) .
        touch "$@"

Then your rules to execute the image can depend on that. You can also use additional Make variables to reduce the repeated parts.

AWS_CREDENTIALS_FILE := $(HOME)/.aws/credentials
DOCKER_RUN_OPTS := -v $(AWS_CREDENTIALS_FILE):/root/.aws/credentials:ro

.PHONY: create run

create: .docker-image
        docker run $(DOCKER_RUN_OPTS) $(DOCKER_IMAGE) create

run: .docker-image
        docker run $(DOCKER_RUN_OPTS) $(DOCKER_IMAGE) run

This creates a hidden file .docker-image that records that the image has been built, but since there's a rule to "build" that file (by building the Docker image and touching it) it can be used as an ordinary dependency.

Upvotes: 1

tripleee
tripleee

Reputation: 189507

Perhaps a more fruitful approach is to encapsulate the command itself in a function.

define yoyo
 docker build -t yoyo --build-arg SCRIPT="$1" . && \
 docker run - ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo
endef

.PHONY: create run

create:
    $(call yoyo,create)

run:
    $(call yoyo,run)

Building a Docker image with the same name with different functionalities depending on build configuration seems rather dubious, though. Perhaps it would make sense to build with different tags, just once?

.PHONY: create run
create run: %: .%-tagged
    docker run --rm - ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo-$*

.%-tagged: Dockerfile
    docker build -t yoyo-$* --build-arg SCRIPT=$* .
    touch $@

Upvotes: 1

Vroomfondel
Vroomfondel

Reputation: 2898

TARGETS := $(sort $(MAKECMDGOALS)) # remove duplicates
.PHONY: $(TARGETS)

$(TARGETS):
    docker build -t yoyo --build-arg SCRIPT=$@ . \
    && docker run -v ${HOME}/.aws/credentials:/root/.aws/credentials:ro yoyo

I question the usefulness of such a makefile however. If you write it as normal script you take away one level of complexity for the programmer coming after you.

Upvotes: 0

Related Questions