Правило вызова другого Makefile изнутри Makefile - PullRequest
0 голосов
/ 10 января 2019

У меня есть Makefile, из которого я запускаю команду другого Makefile, если какой-либо объектный файл уже не существует.

Правило следующее:

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

где переменные определяются следующим образом Makefile:

COMMONDIR     := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON  := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))

Это правило работает нормально, но в конце дня единственным реальным вводом, требуемым правилом, является другой Makefile, поэтому я попытался:

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

но это не работает, почему это так?

Для полноты вот полный Makefile

CC = gcc

INC_PATH = -I../common/

SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR  :=./obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

COMMONDIR     := ../common
SOURCESCOMMON := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON  := $(COMMONDIR)/obj
OBJECTSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

# OBJS_LOC is in current working directory,
EXECUTABLE := ../server
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)

clean:
    $(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)

# Linking the executable from the object files
# $^   # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(OBJECTSCOMMON)
    $(CC) $(WARNING) $^ -o $@

-include $(DEPENDS) $(DEPENDSCOMMON)

$(OBJDIR):
    mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
    $(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@

$(OBJDIRCOMMON):
    mkdir -p $(OBJDIRCOMMON)

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

EDIT

Я получаю такую ​​ошибку:

Entering directory '/home/user/Documents/UnixSystem/network/common'
gcc -Wall -Wextra -MMD -MP -c utilities.c -o obj/utilities.o
gcc -Wall -Wextra -MMD -MP -c error.c -o obj/error.o
make[2]: Leaving directory '/home/user/Documents/UnixSystem/network/common'
gcc   ../common/obj/error.d.o   -o ../common/obj/error.d
gcc: error: ../common/obj/error.d.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.

Насколько я понимаю, выполнение другого Makefile было успешным. Однако после этого он пытается выполнить эту команду gcc ../common/obj/error.d.o -o ../common/obj/error.d, что неправильно, но я не знаю, какое правило и почему оно его генерирует.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Почему то, что вы сделали, было неправильно

Рецепт A :

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

и рецепт B :

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

имеют существенно разные значения и, конечно, не приведут к такому же поведению.

Рецепт A говорит:

  1. Любая цель $(OBJDIRCOMMON)/file.o должна быть обновлена, если она не существует или старше $(COMMONDIR)/file.c или $(COMMONDIR)/Makefile.
  2. Если цель $(OBJDIRCOMMON)/file.o должна быть обновлена, то $(OBJDIRCOMMON) сначала должны быть обновлены.
  3. Чтобы сделать цель $(OBJDIRCOMMON)/file.o актуальной, запустите расширение $(MAKE) -C $(COMMONDIR) в оболочке.

Рецепт B говорит:

  1. Любая цель $(OBJDIRCOMMON)/file.o должна быть обновлена, если она не существует или старше $(COMMONDIR)/Makefile.
  2. Если цель $(OBJDIRCOMMON)/file.o должна быть обновлена, то $(OBJDIRCOMMON) сначала должны быть обновлены.
  3. Чтобы сделать цель $(OBJDIRCOMMON)/file.o актуальной, выполните расширение $(MAKE) -C $(COMMONDIR) в оболочке.

Обратите внимание, что критерий A.1 отличается от критерия B.1 . Рецепт А выполню если $(OBJDIRCOMMON)/file.o старше $(OBJDIRCOMMON)/file.c. Рецепт B не будет. Рецепт B отбрасывает зависимость объектных файлов от соответствующих исходных файлов, и говорит Make, что $(OBJDIRCOMMON)/file.o может быть переделан, только если старше $(COMMONDIR)/Makefile.

в конце дня единственным реальным вводом, требуемым правилом, является другой Makefile

То, что вы подразумеваете здесь под «правилом», на самом деле является командной строкой (расширено от) $(MAKE) -C $(COMMONDIR). Входные данные этой команды - это одно; критерии для его выполнения - другое.

Как то, что вы сделали, вызвало ошибку, которую вы видите .

Это терновее. Давайте воспроизведем это.

Вот манеж:

$ ls -R
.:
app  common

./app:
foo.c  main.c  Makefile

./common:
bar.c  Makefile

Здесь ./app/Makefile - это именно ваш Makefile с рецептом A . ./common/Makefile, который вы не опубликовали, это просто:

obj/bar.o: bar.c
    gcc -MMD -MP -c -I. $< -o $@

потому что это подойдет для иллюстрации.

Мы встраиваем ./app:

$ cd app
$ make
mkdir -p ./obj
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
mkdir -p ../common/obj
make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server

что нормально.

Теперь я изменил ./app/Makefile, как вы сделали, чтобы использовать рецепт B , и перестроить.

$ make
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server

Все еще в порядке ... Но подождите минуту! Тот не вызвал ./common сделать вообще, который может повлиять на изменение. Лучше clean:

$ make clean
rm -f ./obj/foo.o ./obj/main.o ./obj/foo.d ./obj/main.d ../server

и попробуйте снова:

$ make
gcc -Wall -Wextra -MMD -MP -c -I../common/ foo.c -o obj/foo.o
gcc -Wall -Wextra -MMD -MP -c -I../common/ main.c -o obj/main.o
gcc -Wall -Wextra obj/foo.o obj/main.o ../common/obj/bar.o -o ../server

Нет разницы? Ах, это потому, что этот Makefile clean не может удалить все файлы, которые make создает: он исключает ../common/obj/bar.o. Так что я просто:

$ rm ../common/obj/*

И еще один ход:

$ make
make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'
gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d
gcc: error: ../common/obj/bar.d.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.

которая твоя тайна.

Когда я зарезал файлы ../common/obj, я удалил не только все файлы object в нем но также файл зависимости ../common/obj/bar.d. И теперь Make пытается переделать это, запустив:

gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d

Как получилось? Чтобы ответить на это, мы сначала изменим ./app/Makefile обратно на рецепт A - считать это выполненным - и затем сделать:

$ make --print-data-base > out.txt

, который сбрасывает в out.txt всю информацию, которая заставляет gleans читать все make-файлы (Makefile и все make-файлы, которые он рекурсивно include -s, в этом случае только автоматически сгенерированные .d файлы).

Посмотрим, что скажет база данных о ../common/obj/bar.d. Там написано:

# Not a target:
../common/obj/bar.d:
# Implicit rule search has been done.
# Last modified 2019-01-11 16:01:33.199263608
# File has been updated.
# Successfully updated.

Конечно, мы не хотим, чтобы ../common/obj/bar.d были целью, и это не a цель, потому что, прочитав все make-файлы и рассмотрев все встроенные правила, и все файлы, которые он может найти, Make не может увидеть, каким образом ../common/obj/bar.d должен быть обновлен в отношении любого из этих файлов. Хорошо.

Теперь давайте вернемся к рецепту B в ./app/Makefile снова - считайте, что это сделано - и снова сделайте:

$ make --print-data-base > out.txt

и снова посмотрите в out.txt относительно ../common/obj/bar.d. На этот раз мы находим:

../common/obj/bar.d: ../common/obj/bar.d.o
# Implicit rule search has been done.
#  Implicit/static pattern stem: '../common/obj/bar.d'
# Last modified 2019-01-11 16:01:33.199263608
# File has been updated.
# Successfully updated.
#  recipe to execute (built-in):
    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

Итак, на этот раз ../common/obj/bar.d является целью! И это зависит от ../common/obj/bar.d.o! И рецепт, чтобы сделать это:

    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

, который, конечно, расширится до:

gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d

Как Make смог справиться с этим, благодаря рецепту B ?

Ну, во-первых, он рассмотрел, есть ли какие-либо правила в make-файлах или какие-либо из встроенные правила дали прямой способ сделать ../common/obj/bar.d из любых существующих файлов, и нарисовал пробел.

Далее он рассмотрел, дает ли какое-либо из этих правил способ сделать ../common/obj/bar.d из промежуточного файла . Промежуточный файл - это файл, который не существует, но сам по себе может быть создан из существующих файлов, по любому из правил, которые он прочитал, или по своим встроенным правилам. это раз он видел путь.

Одно из встроенных шаблонных правил Make:

%: %.o
#  recipe to execute (built-in):
    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

Вы можете найти его прямо там, в out.txt. И вы можете видеть, что это шаблонное правило, которое соответствует:

../common/obj/bar.d: ../common/obj/bar.d.o

Рецепт есть рецепт ссылка на программу называется ../common/obj/bar.d дано объектный файл ../common/obj/bar.d.o.

Нет объектного файла ../common/obj/bar.d.o, но может ли он быть промежуточным файлом? Если Make может найти правило для создания ../common/obj/bar.d.o из файлов, которые существуют do , тогда он также может сделать ../common/obj/bar.d с этим правилом %: %.o.

И может найти рецепт изготовления ../common/obj/bar.d.o из существующих файлов потому что мы только что дали это один! - рецепт B :

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

Это говорит, что если что-либо соответствует цели $(OBJDIRCOMMON)/%.o (например, ../common/obj/bar.d.o) не существует, но $(COMMONDIR)/Makefile существует (что он делает), то эта цель обновляется с помощью команды:

$(MAKE) -C $(COMMONDIR)

Заставь нас поверить. Это бежало $(MAKE) -C $(COMMONDIR):

make -C ../common
make[1]: Entering directory '/home/imk/develop/so/make_prob/common'
gcc -MMD -MP -c -I. bar.c -o obj/bar.o
make[1]: Leaving directory '/home/imk/develop/so/make_prob/common'

и затем считается ../common/obj/bar.d.o актуальным. Итак, он перешел к:

../common/obj/bar.d: ../common/obj/bar.d.o
    $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@

и побежал:

gcc   ../common/obj/bar.d.o   -o ../common/obj/bar.d

, который потерпел неудачу, потому что мы солгали:

make -C ../common

делает не делает ../common/obj/bar.d.o вообще.

gcc: error: ../common/obj/bar.d.o: No such file or directory

Это не возникает с рецептом A , потому что

$(OBJDIRCOMMON)/bar.d.o: $(OBJDIRCOMMON)/bar.d.c $(COMMONDIR)/Makefile | $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

делает не предложение Сделать способ сделать $(OBJDIRCOMMON)/bar.d.o из существующих файлов, потому что $(OBJDIRCOMMON)/bar.d.c не существует. Так что ../common/obj/bar.d не цель.

Придерживайтесь рецепта A , потому что он правильный, а рецепт B неправильный. Также обзор и исправьте make-файлы так, чтобы make clean всегда удалял все не .PHONY цели, которые могли быть построены, и ничего больше. Наконец, избегайте написания рецептов с не-1271 * целями, где в рецепте не упоминается цель.

0 голосов
/ 11 января 2019

Должно быть && вместо трубы

      $(OBJDIRCOMMON)/%.o: $(COMMONDIR)/Makefile | (OBJDIRCOMMON)
+$(MAKE) -C $(COMMONDIR
...