Linux C++ gmake "Нет правил, чтобы сделать цель"

Прошло пару десятилетий с тех пор, как мне пришлось управлять make-файлами. Я проверил существующие ответы здесь, а также поиск в других местах. Когда я использую цель: $(BIN)/$(EXECUTABLE): $(OBJECTS)Я озадачен тем, почему мой make-файл не работает с:

$ make make: *** No rule to make target 'src/SRecord.o', needed by 'bin/main'. Stop.

Вот Makefile:

# execute `make D='-g'` to make with debug symbols
DEFS := $(D)
CC := gcc $(DEFS)
CFLAGS := -std=c99 -Wall -Wextra
CXX := g++ $(DEFS)
CXXFLAGS := -std=c++17 -Wall -Wextra

BIN := bin
SRC := src
INCLUDE := include
LIB := lib

#LIBSOURCES := $(wildcard $(LIB)/*.cpp)
#LIBOBJECTS := $(LIBSOURCES:%.cpp=%.o)
#OBJECTS := $(subst $(LIB)/,,$(LIBOBJECTS))

SOURCES := $(wildcard $(SRC)/*.cpp)
OBJECTS := $(SOURCES:%.cpp=%.o)

# SOURCES := \
#   $(SRC)/main.cpp \
#   $(SRC)/SerialPort.cpp \
#   $(SRC)/ShadowUpdate.cpp \
#   $(SRC)/SRecord.cpp

# OBJECTS := \
#   $(SRC)/SerialPort.o \
#   $(SRC)/ShadowUpdate.o \
#   $(SRC)/SRecord.o

EXECUTABLE := main

#
# Targets
#
.PHONY: clean all
all: $(BIN)/$(EXECUTABLE)

clean:
    $(RM) $(BIN)/$(EXECUTABLE)
    $(RM) $(OBJECTS)

run: all
    ./$(BIN)/$(EXECUTABLE)

# $(BIN)/$(EXECUTABLE): $(SRC)/*
$(BIN)/$(EXECUTABLE): $(OBJECTS)
    $(CXX) $(CXXFLAGS) -I$(INCLUDE) -L$(LIB) $^ -o $@ 

#
# Dependencies - must be at end of makefile
#
DEPDIR := .d
$(shell mkdir -p $(DEPDIR) >/dev/null)
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td

POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@

%.o: %.c
%.o: %.c $(DEPDIR)/%.d
    $(CC) -c -o $*.o $(DEPFLAGS) $(CFLAGS) $<
    $(POSTCOMPILE)

%.o: %.cpp
%.o: %.cpp $(DEPDIR)/%.d
    $(CXX) -c -o $*.o $(DEPFLAGS) $(CXXFLAGS) $<
    $(POSTCOMPILE)

$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d

include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(SOURCES))))

Работает с закомментированной целью: # $(BIN)/$(EXECUTABLE): $(SRC)/*

Но я хочу, чтобы у меня был более сложный проект, чем просто перечисление всех исходных файлов в качестве зависимостей для исполняемого файла.

Вот мое дерево файлов:

src/
src/SerialPort.cpp
src/SRecord.cpp
src/ShadowUpdate.cpp
src/main.cpp
include
include/ShadowUpdate.h
include/SerialPort.h
include/SRecord.h
include/the_exo.h
bin/
bin/main
Makefile
lib/
.d/

Автоматическая генерация зависимостей тоже не работает, хотя она прямо с сайта GNU.

2 ответа

Решение

Подумай о том, что % расширяется до в

%.o: %.cpp $(DEPDIR)/%.d

подключить src/SRecord.o:

src/SRecord.o: src/SRecord.cpp .d/src/SRecord.d

Вы видите проблему? Попробуйте следующее

$(SRC)/%.o: $(SRC)/%.cpp $(SRC)/$(DEPDIR)/%.d

Вам не понадобятся строки, которые переопределяют встроенные рецепты (%.o: %.cpp), Избавься от них.

Для ясности всем, кто ищет ответ в будущем, я публикую свой пересмотренный Makefile:

BIN := bin
SRC := src
INCLUDE := include
LIB := lib

# execute `make D='-g'` to make with debug symbols
DEFS := $(D)
CC := gcc $(DEFS)
CFLAGS := -I$(INCLUDE) -std=c99 -Wall -Wextra
CXX := g++ $(DEFS)
CXXFLAGS := -I$(INCLUDE) -std=c++17 -Wall -Wextra

#LIBSOURCES := $(wildcard $(LIB)/*.cpp)
#LIBOBJECTS := $(LIBSOURCES:%.cpp=%.o)
#OBJECTS := $(subst $(LIB)/,,$(LIBOBJECTS))

SOURCES := $(wildcard $(SRC)/*.cpp)
OBJECTS := $(SOURCES:%.cpp=%.o)

EXECUTABLE := main

#
# Targets
#
.PHONY: clean all
all: $(BIN)/$(EXECUTABLE)

clean:
    $(RM) $(BIN)/$(EXECUTABLE)
    $(RM) $(OBJECTS)

run: all
    ./$(BIN)/$(EXECUTABLE)

$(BIN)/$(EXECUTABLE): $(OBJECTS)
    $(CXX) $(CXXFLAGS) -L$(LIB) $^ -o $@ 

#
# Dependencies - must be at end of makefile
#
DEPDIR := $(SRC)/.d
$(shell mkdir -p $(DEPDIR) >/dev/null)
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td

POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@

%.o: %.c
%.o: %.c $(DEPDIR)/%.d
    $(CC) -c -o $@ $(DEPFLAGS) $(CFLAGS) $<
    $(POSTCOMPILE)

%.o: %.cpp
$(SRC)/%.o: $(SRC)/%.cpp $(DEPDIR)/%.d
    $(CXX) -c -o $@ $(DEPFLAGS) $(CXXFLAGS) $<
    $(POSTCOMPILE)

$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d

include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(SOURCES))))

Чтобы уточнить мой комментарий к ответу выше, если я закомментирую цель "переопределить" (%.o: %.cpp), вот вывод:

$ make clean; make
rm -f bin/main
rm -f src/SRecord.o src/SerialPort.o src/ShadowUpdate.o src/main.o
g++  -Iinclude -std=c++17 -Wall -Wextra   -c -o src/SRecord.o src/SRecord.cpp
g++  -Iinclude -std=c++17 -Wall -Wextra   -c -o src/SerialPort.o src/SerialPort.cpp
g++  -Iinclude -std=c++17 -Wall -Wextra   -c -o src/ShadowUpdate.o src/ShadowUpdate.cpp
g++  -Iinclude -std=c++17 -Wall -Wextra   -c -o src/main.o src/main.cpp
g++  -Iinclude -std=c++17 -Wall -Wextra -Llib src/SRecord.o src/SerialPort.o src/ShadowUpdate.o src/main.o -o bin/main 

Понятно, что зависимости не создаются. Мое правило не было выполнено.

Но с пустым %.o: %.cpp цель на месте, make выполняет, как ожидалось:

$ make clean; make
rm -f bin/main
rm -f src/SRecord.o src/SerialPort.o src/ShadowUpdate.o src/main.o
g++  -c -o src/SRecord.o -MT src/SRecord.o -MMD -MP -MF src/.d/SRecord.Td -Iinclude -std=c++17 -Wall -Wextra src/SRecord.cpp
g++  -c -o src/SerialPort.o -MT src/SerialPort.o -MMD -MP -MF src/.d/SerialPort.Td -Iinclude -std=c++17 -Wall -Wextra src/SerialPort.cpp
g++  -c -o src/ShadowUpdate.o -MT src/ShadowUpdate.o -MMD -MP -MF src/.d/ShadowUpdate.Td -Iinclude -std=c++17 -Wall -Wextra src/ShadowUpdate.cpp
g++  -c -o src/main.o -MT src/main.o -MMD -MP -MF src/.d/main.Td -Iinclude -std=c++17 -Wall -Wextra src/main.cpp
g++  -Iinclude -std=c++17 -Wall -Wextra -Llib src/SRecord.o src/SerialPort.o src/ShadowUpdate.o src/main.o -o bin/main 
Другие вопросы по тегам