在子目录中运行"make":"No rule to make target"



我正试图让GNUMake在子目录中运行一些规则。简而言之:我有一个项目,有多个Python源目录(AWS lambdas(,每个目录都有一个pyproject.toml文件,依赖安装由poetry管理。对于每个子目录,我需要安装pyproject.toml中提到的包,它创建了一个poetry.lock文件,然后用于生成requirements.txt文件(部署到AWS并在那里使用(。

到目前为止,我得到的是:

POETRY := $(shell command -v poetry 2> /dev/null)
PY_LAMBDAS := $(dir $(wildcard src/python/*/pyproject.toml))
.PHONY: $(PY_LAMBDAS)
install-py-lambdas : $(PY_LAMBDAS)
$(PY_LAMBDAS) :
$(MAKE) -C $@ requirements.txt
requirements.txt : poetry.lock
$(POETRY) export --without-hashes --format=requirements.txt > requirements.txt
# Ignore for now
# poetry.lock : pyproject.toml
#   $(POETRY) install
# pyproject.toml : | $(VENV)/bin/python
#   $(PY8) -m venv $(VENV)

如果lambda目录中有一个requirements.txt文件,它会显示Nothing to be done for 'requirements.txt',这似乎很好。如果没有requirements.txt文件,它将错误为:No rule to make target 'requirements.txt'. Stop.正如我现在测试的那样,poetry.lock文件已经存在于所有目录中。

我的最佳猜测是,规则在子目录中运行的事实导致了找不到目标和规则的某种失败,我不知道。我需要在子目录中运行规则,因为poetry从当前目录中读取其环境,并且没有任何类型的"目标目录";选项

我希望这对我来说是一个相对简单的错误!我的基本问题的任何替代解决方案都将非常受欢迎。

这里的关键是要有单独的顶级Makefile和子目录Makefile,前者处理递归,后者处理实际构建。

相关资源:

  • GNU Make手册:Make的递归使用
  • 一个SO答案显示了如何实现递归

下面你会发现一个简化的例子,应该可以解决你的情况。我不了解Python,所以我用简单的命令替换了与Python相关的命令,但从make的角度来看,这应该没有什么区别——这是一个常见的make模式。

目录结构:

$ exa --tree --long --no-permissions --no-user --no-time
- .
- ├── dir1
171 │  ├── Makefile
0 │  └── pyproject.toml
- ├── dir2
171 │  ├── Makefile
0 │  └── pyproject.toml
187 └── Makefile

处理递归并将公共变量传递给子make:的顶级Makefile

$ cat Makefile 
export BUILD_COMMAND_1 = echo `date --iso-8601=seconds`
export BUILD_COMMAND_2 = cat
PROJECTS := $(wildcard dir?)
.PHONY: all $(PROJECTS)
all: $(PROJECTS)
$(PROJECTS):
$(MAKE) -C $@

所有子目录都有相同的Makefile(这里我们只显示dir1/Makefile(:

$ cat dir1/Makefile
requirements.txt: poetry.lock
$(BUILD_COMMAND_2) $< > $@
poetry.lock: pyproject.toml
$(BUILD_COMMAND_1) > $@
.PHONY: clean
clean:
rm -rf poetry.lock requirements.txt

当顶层Makefile第一次运行时,所有目标都被构建:

$ make
make -C dir1
make[1]: Entering directory '/tmp/t/dir1'
echo `date --iso-8601=seconds` > poetry.lock
cat poetry.lock > requirements.txt
make[1]: Leaving directory '/tmp/t/dir1'
make -C dir2
make[1]: Entering directory '/tmp/t/dir2'
echo `date --iso-8601=seconds` > poetry.lock
cat poetry.lock > requirements.txt
make[1]: Leaving directory '/tmp/t/dir2'

当顶层Makefile第二次运行并且预需求没有改变时,不需要构建任何东西:

$ make
make -C dir1
make[1]: Entering directory '/tmp/t/dir1'
make[1]: 'requirements.txt' is up to date.
make[1]: Leaving directory '/tmp/t/dir1'
make -C dir2
make[1]: Entering directory '/tmp/t/dir2'
make[1]: 'requirements.txt' is up to date.
make[1]: Leaving directory '/tmp/t/dir2'

将目标传给子目标的变体

  • GNU Make Manual:指定目标的论据

顶级Makefile:

$ cat Makefile 
export BUILD_COMMAND_1 = echo `date --iso-8601=seconds`
export BUILD_COMMAND_2 = cat
PROJECTS := $(wildcard dir?)
.PHONY: requirements.txt clean $(PROJECTS)
requirements.txt: $(PROJECTS)
clean: $(PROJECTS)
$(PROJECTS):
$(MAKE) -C $@ $(MAKECMDGOALS)

最新更新