使用%通配符的Make目标无法正常执行,提示‘Nothing to be done’
这个问题我遇见过,核心问题出在两个地方:没有把FORCE标记为伪目标(.PHONY),以及当前的依赖规则不够精确。
1. FORCE目标未被声明为.PHONY
你写的FORCE: ;只是创建了一个带空命令的目标,但GNU Make默认会把它当作实际的文件目标处理。如果当前目录里没有名为FORCE的文件,Make第一次运行时会执行这个空命令,然后标记FORCE为“已完成”;后续运行时,Make会认为FORCE目标已经是最新的,不会触发依赖它的bin/%目标重新构建。
解决方法很简单:把FORCE声明为.PHONY目标,告诉Make这个目标不是实际文件,而是一个总是需要被“重新构建”的标记:
.PHONY: FORCE build
2. 依赖规则依赖目录而非源码文件
你的规则bin/%: cmd/% FORCE依赖的是cmd/%目录,但Unix系统中,目录的修改时间(mtime)只会在目录内的文件被添加、删除时更新,修改文件内容不会改变目录的mtime。这意味着如果你修改了cmd/my-app/main.go的内容,Make会认为cmd/my-app目录的时间没有变化,即使有FORCE也可能出现预期外的行为(当然加上.PHONY FORCE后会强制构建,但依赖具体文件更合理)。
建议把依赖改成具体的源码文件cmd/%/main.go,这样只要源码文件修改,Make就会自动触发构建:
bin/%: cmd/%/main.go FORCE $(BUILD_BINARY)
对应的,BUILD_BINARY宏里的./$<需要调整,因为现在$<指向的是main.go文件而非目录,你可以用$*匹配目标中的%部分(也就是应用名称),写法更清晰:
define BUILD_BINARY @echo go build $(GO_BUILD_FLAGS) -o $@ ./cmd/$* go build $(GO_BUILD_FLAGS) -o $@ ./cmd/$* endef
修正后的完整Makefile
GO_BUILD_FLAGS = APPS = my-app another-app stuff define BUILD_BINARY @echo go build $(GO_BUILD_FLAGS) -o $@ ./cmd/$* go build $(GO_BUILD_FLAGS) -o $@ ./cmd/$* endef .PHONY: FORCE build bin/%: cmd/%/main.go FORCE $(BUILD_BINARY) build: $(addprefix bin/,$(APPS))
现在运行make build或者make bin/my-app,就能正常触发编译了。如果想要强制重新构建(哪怕源码没改),这个配置也能满足需求;如果不需要强制,只在源码变化时构建,可以去掉FORCE依赖和.PHONY: FORCE。
内容的提问来源于stack exchange,提问作者Marco




