#makefile
#makefile
Вопрос:
Я написал makefile для компиляции для сгенерированного файла
%.o: %.cc
g -c $< -o $@
default: gen main.o
gen:
touch main.cc
И получил
$ make default
touch main.cc
make: *** No rule to make target 'main.o', needed by 'default'. Stop.
Но если я добавлю папку для сгенерированных файлов .cc и .o, это сработает
obj/%.o: src/%.cc
g -c $< -o $@
default: gen obj/main.o
gen:
touch src/main.cc
Почему запрашиваются папки obj и src
Ответ №1:
Это не так. Такое поведение связано с make
оптимизацией и тем, что вы Makefile
неискренни в том, что он делает.
При make
чтении каталога он хэширует все содержимое каталога и выполняет будущие поиски файлов в этом хэше. При make
первом запуске main.cc
файла нет, поэтому хэш каталога не содержит записи для main.cc
. Поскольку ваш Makefile
генерирует этот файл, но не объявляет его, make
не знает, что существует цель, которая генерирует main.cc
файл, и когда он ищет в хэше, файла там нет. Поэтому ваше неявное правило отклоняется как невозможное:
$ make -dr default
...
Considering target file 'default'.
File 'default' does not exist.
Looking for an implicit rule for 'default'.
No implicit rule found for 'default'.
Considering target file 'gen'.
File 'gen' does not exist.
Finished prerequisites of target file 'gen'.
Must remake target 'gen'.
touch main.cc
Putting child 0x55de3daaa6a0 (gen) PID 6075 on the chain.
Live child 0x55de3daaa6a0 (gen) PID 6075
Reaping winning child 0x55de3daaa6a0 PID 6075
Removing child 0x55de3daaa6a0 PID 6075 from chain.
Successfully remade target file 'gen'.
Considering target file 'main.o'.
File 'main.o' does not exist.
Looking for an implicit rule for 'main.o'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Looking for a rule with intermediate file 'main.cc'.
Avoiding implicit rule recursion.
No implicit rule found for 'main.o'.
Finished prerequisites of target file 'main.o'.
Must remake target 'main.o'.
make: *** No rule to make target 'main.o', needed by 'default'. Stop.
Если вы strace
выполните этот вызов, вы заметите две вещи: а) getdents64
вызов, который считывает содержимое каталога (используется для поиска и хэширования) и б) отсутствие stat
for main.cc
вообще, что означает, что на диске нет проверки на наличие файла, поскольку его наличие было разрешено из кэша:
$ strace make -r default
...
stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getdents64(3, /* 3 entries */, 32768) = 80
getdents64(3, /* 0 entries */, 32768) = 0
close(3) = 0
...
stat("gen", 0x7fff4f66a3d0) = -1 ENOENT (No such file or directory)
stat("main.o", 0x7fff4f66a460) = -1 ENOENT (No such file or directory)
... <-- locale-related calls for error message
write(2, "make: *** No rule to make target"..., 71make: *** No rule to make target 'main.o', needed by 'default'. Stop.
Теперь, если вы повторите ту же команду, она будет успешной, потому main.cc
что файл уже создан и доступен, когда каталог кэшируется:
$ make -dr default
...
Considering target file 'default'.
File 'default' does not exist.
Looking for an implicit rule for 'default'.
No implicit rule found for 'default'.
Considering target file 'gen'.
File 'gen' does not exist.
Finished prerequisites of target file 'gen'.
Must remake target 'gen'.
touch main.cc
Putting child 0x555646acc6a0 (gen) PID 6079 on the chain.
Live child 0x555646acc6a0 (gen) PID 6079
Reaping winning child 0x555646acc6a0 PID 6079
Removing child 0x555646acc6a0 PID 6079 from chain.
Successfully remade target file 'gen'.
Considering target file 'main.o'.
File 'main.o' does not exist.
Looking for an implicit rule for 'main.o'.
Trying pattern rule with stem 'main'.
Trying implicit prerequisite 'main.cc'.
Found an implicit rule for 'main.o'.
Considering target file 'main.cc'.
Looking for an implicit rule for 'main.cc'.
No implicit rule found for 'main.cc'.
Finished prerequisites of target file 'main.cc'.
No need to remake target 'main.cc'.
Finished prerequisites of target file 'main.o'.
Must remake target 'main.o'.
g -c main.cc -o main.o
Putting child 0x555646ad0690 (main.o) PID 6080 on the chain.
Live child 0x555646ad0690 (main.o) PID 6080
Reaping winning child 0x555646ad0690 PID 6080
Removing child 0x555646ad0690 PID 6080 from chain.
Successfully remade target file 'main.o'.
Finished prerequisites of target file 'default'.
Must remake target 'default'.
Successfully remade target file 'default'.
Если вы играете по правилам и пишете Makefile
правильно, объявление создаваемых файлов make
будет выполняться должным образом:
$ cat Makefile
%.o: %.cc
g -c $< -o $@
default: main.o
main.cc:
touch $@
$ make default
touch main.cc
g -c main.cc -o main.o