Makefile добавляет себя в качестве цели - PullRequest
6 голосов
/ 24 ноября 2010

У меня есть Makefile для программы на C ++, которая использует автоматическую генерацию зависимостей.Рецепт% .d взят из руководства GNU Make.

Проблема в том, что каким-то образом «Makefile» добавляется в качестве цели, а затем неявное правило заставляет его предполагать, что это исполняемый файл и использует мой src./%.cpp правило, чтобы попытаться скомпилировать src / Makefile.cpp.При просмотре отладочной информации это всегда происходит сразу после выполнения включения.

    No need to remake target `build/Sprite.d'.
 Considering target file `Makefile'.
  Looking for an implicit rule for `Makefile'.
  ...
  Trying pattern rule with stem `Makefile'.
  Trying implicit prerequisite `Makefile.o'.
  Looking for a rule with intermediate file `Makefile.o'.

Я знаю, что включение вызывает перестроение данных Makefile в случае необходимости.Это также пытается восстановить текущий Makefile?Если да, то как мне остановить его, а если нет, то почему «Makefile» добавляется в качестве цели?

Кроме того, выполняется включение, в результате чего файлы .d переделываются, даже если я указываюцель в командной строке, например make clean.Есть ли способ остановить это?



# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src/
INCLUDE_DIR = include/
BUILD_DIR = build/

SOURCES = $(wildcard $(SOURCE_DIR)*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
 $(CXX) $(LIBS) $^ -o $(MAIN)

include $(DEPENDENCIES)

%.o: $(BUILD_DIR)stamp
 $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

$(BUILD_DIR)%.d: $(SOURCE_DIR)%.cpp $(BUILD_DIR)stamp
 @ echo Generate dependencies for $ $@.$$$$; \
 sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g'  $@; \
 rm -f $@.$$$$

$(BUILD_DIR)stamp:
 mkdir -p $(BUILD_DIR)
 touch $@

.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
 @ echo $(SOURCES)
 @ echo $(OBJECTS)
 @ echo $(DEPENDENCIES)


1 Ответ

12 голосов
/ 24 ноября 2010

Make всегда будет пытаться переделать Makefile перед выполнением Makefile. Для этого make будет искать правила, которые можно использовать для воссоздания Makefile. Make будет искать довольно много неявных правил и других непонятных методов для (пере) создания Makefile.

В вашем случае make каким-то образом решил, что шаблонное правило %.o: $(BUILD_DIR)/stamp должно использоваться для воссоздания Makefile, который не удался.

Чтобы предотвратить повторный сбор Makefile в make, вы можете написать правило с пустым рецептом:

Makefile: ;

Прочтите главу Переработка файлов Makefile в руководстве по сборке для более подробного объяснения.

О включенных Makefiles: Включенные Makefile всегда будут включены, независимо от цели. Если включенные make-файлы отсутствуют (или старше, чем их предварительные требования), то они сначала будут (повторно) созданы. Это означает, что make clean сначала сгенерирует .d Make-файлы, только чтобы удалить их снова.

Вы можете предотвратить включение для определенных целей, заключив директиву include в условное выражение:

ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

Вот весь ваш Makefile с некоторыми исправлениями. Я отметил места, где я что-то изменил.

# Makefile

# $(call setsuffix,newsuffix,files)
# Replaces all the suffixes of the given list of files.
setsuffix = $(foreach file,$2,$(subst $(suffix $(file)),$1,$(file)))

# $(call twinfile,newdir,newsuffix,oldfile)
# Turns a path to one file into a path to a corresponding file in a different
# directory with a different suffix.
twinfile = $(addprefix $1/,$(call setsuffix,$2,$(notdir $3)))

MAIN = main

SOURCE_DIR = src
INCLUDE_DIR = include
BUILD_DIR = build

SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp)
OBJECTS = $(call twinfile,$(BUILD_DIR),.o,$(SOURCES))
DEPENDENCIES = $(call twinfile,$(BUILD_DIR),.d,$(SOURCES))

CXX = g++
LIBS = -lpng
CXXFLAGS = -I $(INCLUDE_DIR)


.PHONY: all
all: $(MAIN)

$(MAIN): $(OBJECTS)
    $(CXX) $(LIBS) $^ -o $(MAIN)

# -------> only include if goal is not clean <---------
ifneq ($(MAKECMDGOALS),clean)
include $(DEPENDENCIES)
endif

# ---------> fixed this target <--------------
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    $(CXX) $(CXXFLAGS) -c $(call twinfile,$(SOURCE_DIR),.cpp,$@) -o $@

# ---------> and this target <---------------
$(BUILD_DIR)/%.d: $(SOURCE_DIR)/%.cpp $(BUILD_DIR)/stamp
    @ echo Generate dependencies for $@;
    @set -e; rm -f $@; \
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,$(BUILD_DIR)\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

$(BUILD_DIR)/stamp:
    mkdir -p $(BUILD_DIR)
    touch $@

.PHONY: clean
clean:
    rm -rf $(BUILD_DIR)

.PHONY: printvars
printvars:
    @ echo $(SOURCES)
    @ echo $(OBJECTS)
    @ echo $(DEPENDENCIES)
...