【问题标题】:Makefile:108: *** recipe commences before first targetMakefile:108: *** 配方在第一个目标之前开始
【发布时间】:2019-10-28 20:32:29
【问题描述】:

为 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):;@:) 报错?

【问题讨论】:

  • make tag 0.1 latest ... 通常意味着“更新目标tag, 0.1, latest ...”。有一个黑客攻击,但是(你注意到了吗?)有人评论了它!
  • 因为你把它注释掉了?
  • @mosvy 我知道它被评论了,我不评论然后 eval 错误
  • 您应该编辑您的问题,以便它与您正在调试的真正错误有关,而不是在您注释掉非工作行时发生的一些“红鲱鱼”。
  • @kaz 问题已编辑...使用 eval

标签: linux makefile eval gnu-make gnu


【解决方案1】:

发生该特定错误是因为您的 $(eval ...) 行被一个 TAB 缩进(它被这个可怕的损坏的网络界面隐藏了)。

例子:

$ 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

错误记录在makemanual

recipe commences before first target. Stop.

这意味着 makefile 中的第一件事似乎是 recipe:它以 recipe 前缀字符开头并且不出现 成为合法的make 指令(例如变量赋值)。 食谱必须始终与目标相关联。

“配方前缀字符”默认为 TAB。

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

它不一定是“makefile 中的第一件事”:如果前面有诸如宏赋值之类的指令,同样的错误将在多个规则之后触发:

$ make -f <(printf 'all:;\nkey=val\n\tfoo')
/dev/fd/63:3: *** 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

【讨论】:

  • 如何避免这个标签问题
  • 用空格替换制表符?
  • 为什么标签问题只针对那行代码?为什么选项卡不会为其他代码行创建问题?
  • 所有其他以制表符缩进的行都是配方或指令,例如foo := bar$(error ...)ifdef 跳过。这已经在我的回答的手动引用中进行了解释。
  • ifeq 条件指令在执行标记目标之前执行吗?
【解决方案2】:

我无法重现此问题。我将您最后的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 之类的方法来处理此问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    相关资源
    最近更新 更多