英文:
Makefile order is never satisfied
问题
I have the following project Structure:
| .
| ..
|
| Makefile
| a
| .
| ..
| Dockerfile
\ Makefile
\ b
| .
| ..
| Dockerfile
\ Makefile
a/Makefile:
all:
docker build -t a-image .
b/Makefile:
all:
docker build -t b-image .
b/Dockerfile:
FROM a-image
...
Makefile:
all: a b
a:
make -C a/ all
b: a
make -C b/ all
For some reason, when I run make all
in the root directory of the project, I never get a
built and receive an error while building b
as it depends on a
:
...
pull access denied for a-image, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
...
What am I doing wrong?
英文:
I have the following project Structure:
| .
| ..
|
| Makefile
| a
| .
| ..
| Dockerfile
\ Makefile
\ b
| .
| ..
| Dockerfile
\ Makefile
a/Makefile:
all:
<TAB>docker build -t a-image .
b/Makefile:
all:
<TAB>docker build -t b-image .
b/Dockerfile:
FROM a-image
...
Makefile:
all: a b
a:
<TAB>make -C a/ all
b: a
<TAB>make -C b/ all
For some reason, when I run make all
in the root directory of the ptoject, I never get a
built and receive an error while building b
as it depends on a
.
...
pull access denied for a-image, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
...
What am I doing wrong?
答案1
得分: 4
Make的主要操作方式是围绕着“文件”展开的。您的Makefile会告诉它,如果文件a
不存在,那么创建它的规则在这里;如果b
不存在,a
必须存在(如果需要,可以构建它),并且在这里有创建它的规则。
然而,在您的示例中,a
和b
都是已经存在于磁盘上的目录。Make能够查看磁盘并说“一个名为a
的文件系统条目已经存在”,然后跳过相应的规则。
如果您正在使用GNU Make,并且只是想将Make用作脚本运行器而不是构建系统,您可以将目录规则声明为.PHONY
。这将导致Make忽略文件已经存在的事实,并始终将这些规则视为过时。
# 在顶层Makefile中,不缩进的位置
.PHONY: a b
我建议更好的方法是接受Make的面向文件的模型。无论何时执行某个操作,请确保创建一个文件并使用touch(1)来更新其时间戳,以记录这一事实。例如,在a/Makefile
中,您可以使用一个标志文件来记录镜像构建:
.PHONY: all
all: .docker-build
.docker-build: Dockerfile
docker build -t a-image .
touch "$@"
现在,在父级Makefile中,您知道make -C a
将创建a/.docker-build
,并且您可以将其用作依赖目标。
.PHONY: all
all: b/.docker-build
a/.docker-build:
$(MAKE) -C a
b/.docker-build: a/.docker-build
$(MAKE) -C b
如果删除.PHONY
行,这将与任何Make实现一起工作。
英文:
Make's principal mode of operations is around files. Your Makefile says, if a file a
doesn't exist, here are rules to create it; and if b
doesn't exist, a
must (build it if necessary), and here are rules to create it.
In your example, though, both a
and b
are directories that exist on disk. Make is able to look at the disk and say "a filesystem entry named a
already exists" and skip the corresponding rules.
If you're using GNU Make and are just trying to use Make as a script runner rather than a build system, you can declare the directory rules as .PHONY
. This will cause Make to ignore the fact that the files already exist, and always consider those rules out-of-date.
# in the top-level Makefile, anywhere, unindented
.PHONY: a b
I might suggest a better approach is to embrace Make's file-oriented model. Whenever you do something, make sure you create a file and update its timestamp with touch(1) to record that fact. In a/Makefile
, for example, you could use a sentinel file to record the image build
.PHONY: all
all: .docker-build
.docker-build: Dockerfile
docker build -t a-image .
touch "$@"
Now in the parent Makefile, you know that make -C a
will create a/.docker-build
, and you can use that as the dependency target.
.PHONY: all
all: b/.docker-build
a/.docker-build:
$(MAKE) -C a
b/.docker-build: a/.docker-build
$(MAKE) -C b
If you delete the .PHONY
lines this will work with any Make implementation.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论