Makefile:108:***在第一个目标之前开始配方

问题描述 投票:0回答:3

[为x86_64-pc-linux-gnu构建的GNU Make 4.1

下面是Makefile

# Project variables
PROJECT_NAME ?= todobackend
ORG_NAME ?= shamdockerhub
REPO_NAME ?= todobackend

# File names
DEV_COMPOSE_FILE := docker/dev/docker-compose.yml
REL_COMPOSE_FILE := docker/release/docker-compose.yml

# Docker compose project names
REL_PROJECT := $(PROJECT_NAME)$(BUILD_ID)
DEV_PROJECT := $(REL_PROJECT)dev

# Check and inspect logic
INSPECT := $$(docker-compose -p $$1 -f $$2 ps -q $$3 | xargs -I ARGS docker inspect -f "{{ .State.ExitCode }}" ARGS)

CHECK := @bash -c '\
    if [[ $(INSPECT) -ne 0 ]]; \
    then exit $(INSPECT); fi' VALUE

# Use these settings to specify a custom Docker registry
DOCKER_REGISTRY ?= docker.io

APP_SERVICE_NAME := app

.PHONY: test build release clean tag

test: # Run unit & integration test cases
    ${INFO} "Pulling latest images..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) pull
    ${INFO} "Building images..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) build cache
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) build --pull test
    ${INFO} "Ensuring database is ready..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) run --rm agent
    ${INFO} "Running tests..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) up test
    @ docker cp $$(docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) ps -q test):/reports/. reports
    ${CHECK} ${DEV_PROJECT} ${DEV_COMPOSE_FILE} test
    ${INFO} "Testing complete"

build: # Create deployable artifact and copy to ../target folder
    ${INFO} "Creating builder image..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) build builder
    ${INFO} "Building application artifacts..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) up builder
    ${CHECK} ${DEV_PROJECT} ${DEV_COMPOSE_FILE} builder
    ${INFO} "Copying artifacts to target folder..."
    @ docker cp $$(docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) ps -q builder):/wheelhouse/. target
    ${INFO} "Build complete"

release: # Creates release environment, bootstrap the environment  
    ${INFO} "Building images..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) build webroot
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) build app
    ${INFO} "Ensuring database is ready..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) run --rm agent
    ${INFO} "Collecting static files..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) run --rm app manage.py collectstatic --noinput
    ${INFO} "Running database migrations..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) run --rm app manage.py migrate --noinput
    ${INFO} "Pull external image and build..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) build --pull nginx
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) pull test
    ${INFO} "Running acceptance tests..."
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) up test
    ${CHECK} $(REL_PROJECT) $(REL_COMPOSE_FILE) test
    @ docker cp $$(docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) ps -q test):/reports/. reports
    ${INFO} "Acceptance testing complete"

clean:
    ${INFO} "Destroying development environment..."
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) kill
    @ docker-compose -p $(DEV_PROJECT) -f $(DEV_COMPOSE_FILE) rm -f -v
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) kill
    @ docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) rm -f -v
    @ docker images -q -f dangling=true -f label=application=$(REPO_NAME) | xargs -I ARGS docker rmi -f ARGS
    ${INFO} "Clean complete"

tag:
    $(INFO) "Tagging release image with tags $(TAG_ARGS)"
    @ $(foreach tag, $(TAG_ARGS), docker tag $(IMAGE_ID) $(DOCKER_REGISTRY)/$(ORG_NAME)/$(REPO_NAME):$(tag);)
    ${INFO} "Tagging complete"


# Cosmetics 
YELLOW := "\e[1;33m"
NC := "\e[0m"

# Shell functions
INFO := @bash -c '\
    printf $(YELLOW); \
    echo "=> $$1"; \
    printf $(NC)' VALUE

# Get container id of application service container
APP_CONTAINER_ID := $$(docker-compose -p $(REL_PROJECT) -f $(REL_COMPOSE_FILE) ps -q $(APP_SERVICE_NAME))

# Get image id of application service
IMAGE_ID := $$(docker inspect -f '{{ .Image }}' $(APP_CONTAINER_ID))

# Extract tag arguments
ifeq (tag, $(firstword $(MAKECMDGOALS)))
    TAG_ARGS := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS))
    ifeq ($(TAG_ARGS),)
        $(error You must specify a tag)
    endif
    $(eval $(TAG_ARGS):;@:) # line 108 Do not interpret "0.1 latest whatever" as make target files 
endif

以下是运行make命令时的错误:

$ make tag 0.1 latest $(git rev-parse --short HEAD)
Makefile:108: *** recipe commences before first target.  Stop.

第108行,$(eval $(TAG_ARGS):;@:)的目的是传达0.1 latest $(git rev-parse --short HEAD)不是make目标。


为什么$(eval $(TAG_ARGS):;@:)出错?

linux makefile eval gnu-make gnu
3个回答
0
投票
为什么$(eval $(TAG_ARGS):;@:)不能达到目的,因为它已被注释掉:

# $(eval $(TAG_ARGS):;@:) # Do not interpret "0.1 latest whatever" as make target files

语法着色可以帮助发现这种情况。

0
投票
我无法重现此问题。我将您的最后一个ifeq语句放入了一个makefile中,对于GNU make 4.1和4.2.1,它对我来说很好用。您的情况肯定有一些更不寻常的情况。

调试eval问题的经典方法是复制该行,并将eval替换为info;这样一来,make就会完全打印出所看到的内容。通常,这会向您显示出什么问题。

关于此makefile还有其他令人困惑的事情。

首先,为什么首先在这里使用eval?为什么不直接写规则呢?没错:

$(TAG_ARGS):;@:

无需将其包装在eval中。

[其次,为什么要使用:=然后转义变量?为什么不只使用=而又不去使用转义符?

INSPECT = $(docker-compose -p $1 -f $2 ps -q $3 | xargs -I ARGS docker inspect -f "{{ .State.ExitCode }}" ARGS)

效果很好。

最后,我强烈建议您不要在食谱中添加@。这使得调试makefile非常困难且令人沮丧。而是考虑使用诸如Managing Recipe Echoing之类的方法来处理此问题。


0
投票
发生此特定错误是因为您的$(eval ...)行被TAB缩进了(某些东西被这个可怕的Web界面隐藏了。)>

示例:

$ make -f <(printf '\t$(eval foo:;echo yup)') /dev/fd/63:1: *** recipe commences before first target. Stop. # now with spaces instead of TAB $ make -f <(printf ' $(eval foo:;echo yup)') echo yup yup

make manual中记录了该错误:

recipe commences before first target. Stop.

这意味着makefile中的第一件事似乎是食谱:它以

食谱前缀字符

开头,没有出现成为合法的make指令(例如变量分配)。配方必须始终与目标关联。默认情况下,“食谱前缀字符”为TAB。

$ make -f <(printf '\tfoo') /dev/fd/63:1: *** recipe commences before first target. Stop.

即使宏扩展为空字符串,GNU make也不会考虑仅包含宏扩展为空字符串的行为空:

$ make -f <(printf '\t\nfoo:;@:') $ make -f <(printf '\t$(info foo)\nfoo:;@:') /dev/fd/63:1: *** recipe commences before first target. Stop. $ make -f <(printf ' $(info foo)\nfoo:;@:') foo

© www.soinside.com 2019 - 2024. All rights reserved.