Ошибка Makefile при включении файлов зависимостей, созданных из другого Makefile - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть текущие структуры каталогов:

  • . / MyFolder /
    • Makefile
    • main. c
  • . / Common /
    • Makefile
    • error. c
    • error.h
  • . / Build /
    • / common /
    • / myFolder /

/build/common содержит файлы объектов и зависимостей, полученные при компиляции папки common и эквивалент для build/myFolder.

В основном /myFolder/ содержит программу, для которой требуется некоторая функция из common. Таким образом, /myFolder/Makefile вызывает также /common/Makefile для компиляции.

./common/Makefile - это следующее:

CC = gcc

CURRENT_DIR := $(shell basename $(CURDIR))
SOURCEDIR   := ./
SOURCES     := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR      := ../build/$(CURRENT_DIR)

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

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

all: $(OBJECTS)

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

-include $(DEPENDS) 

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

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

и ./myFolder/Makefile - это следующее:

CC = gcc

INC_PATH       := -I../common/
BUILD_DIR_NAME := build
BUILDDIR       := ../$(BUILD_DIR_NAME)
CURR_DIR_NAME  := $(shell basename $(CURDIR))
SOURCEDIR      := ./
SOURCES        := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR         := $(BUILDDIR)/$(CURR_DIR_NAME)
OBJECTS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

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

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

EXECUTABLE := ../out_file

# .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)
    +$(MAKE) -C $(COMMONDIR) clean

# 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)

Если я войду в папку myFolder и скомпилирую с make в первый раз все ок. Если затем я попробую снова, я получу следующую ошибку:

make: *** No rule to make target `error.c', needed by `../build/common/error.o'.  Stop.

Если я сделаю make clean, а затем make, это сработает.

Строка, вызывающая проблему, следующая:

-include $(DEPENDS) $(DEPENDSCOMMON)

особенно $(DEPENDSCOMMON). С -include $(DEPENDS) вместо него он работает отлично.

Теперь - говорит Makefile не жаловаться, если файлы не существуют. Если они существуют, они будут включены и перекомпилировать ваши источники в соответствии с зависимостями. По этой причине я ожидал, что не скомпилируется с первой попытки, но если он скомпилируется с первой попытки, также не будет генерировать такую ​​ошибку (поскольку все файлы существуют).

Может кто-нибудь сказать, пожалуйста, кто я? отсутствует?

Для полноты вот другие файлы, которые являются только примерами:

main. c

#include "../common/error.h"
#include <stdio.h>


int main(int argc, char *argv[])
{
    if (argc<1) err_sys("some error");
    printf("Hello world\n");
    return 0;
}

ошибка . c

#include <stdio.h>

void err_sys(const char *fmt, ...)
{
    printf("some err\n");
}

error.h

#ifndef _ERROR_H_
#define _ERROR_H_
#endif

/*Fatal error related to a system cal1.
* Print a message and terminate*/
void err_sys(const char *fmt,...);

1 Ответ

2 голосов
/ 15 апреля 2020

Ошибка связана с вашей структурой Makefile.

Основная проблема заключается в том, что вы генерируете зависимости в common/Makefile и используете его в myFolder/Makefile. Зависимости создаются для $(SOURCEDIR)/%.c, например, ./somefile.c, который действителен только в common, а не в myFolder

. Связанная проблема заключается в том, что вы дублируете множество деталей из common/Makefile в myFolder/Makefile , В этой ситуации не имеет особого смысла иметь отдельный Makefile.

Один из способов решить эту проблему - собрать библиотеку из файлов в common и ссылаться только на библиотеку из myFolder/Makefile. Это позволяет избежать дублирования информации от common/Makefile до myFolder/Makefile. Я изменил ваши Makefiles, чтобы работать таким образом. (Возможно, есть место для улучшения, я только хотел заставить его работать.)

common/Makefile:

CC = gcc

CURRENT_DIR := $(shell basename $(CURDIR))
SOURCEDIR   := ./
SOURCES     := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR      := ../build/$(CURRENT_DIR)

LIBNAME=libcommon.a
COMMONLIB = $(OBJDIR)/$(LIBNAME)

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

.PHONY: all clean

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

all: $(COMMONLIB)

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

-include $(DEPENDS)

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

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


$(COMMONLIB): $(OBJECTS)
        $(AR) $(ARFLAGS) $@ $^

myFolder/Makefile:

CC = gcc

INC_PATH       := -I../common/
BUILD_DIR_NAME := build
BUILDDIR       := ../$(BUILD_DIR_NAME)
CURR_DIR_NAME  := $(shell basename $(CURDIR))
SOURCEDIR      := ./
SOURCES        := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR         := $(BUILDDIR)/$(CURR_DIR_NAME)
OBJECTS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

COMMONDIR_NAME := common
COMMONDIR      := ../$(COMMONDIR_NAME)

OBJDIRCOMMON   := $(BUILDDIR)/$(COMMONDIR_NAME)

LIBNAME=libcommon.a
COMMONLIB = $(OBJDIRCOMMON)/$(LIBNAME)


.PHONY: buildlib clean all

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

EXECUTABLE := ../out_file

# .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)
    +$(MAKE) -C $(COMMONDIR) clean

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

-include $(DEPENDS)

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

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

$(COMMONLIB): FORCE
    +$(MAKE) -C $(COMMONDIR)

FORCE:

Примечание : Я использовал цель FORCE без правила в качестве зависимости вместо объявления $(COMMONLIB) как .PHONY для принудительного запуска рекурсивного make. Когда я пытался с .PHONY, исполняемый файл каждый раз перестраивался без проверки метки времени файла библиотеки.


Другой вариант - выбросить Makefile в common и заменить рекурсивный make вызов myFolder/Makefile с соответствующей командой компиляции.

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c Makefile| $(OBJDIRCOMMON)
    $(CC) $(WARNING) -MMD -MP -c $< -o $@

Примечания

Вам не нужно использовать относительный путь ../common в директиве #include, когда вы указываете это как каталог включения с опцией -I.

Я добавил .PHONY, чтобы объявить ваши фальшивые цели как таковые.

Для Automati c Генерация зависимостей и общие рекомендации по Make-файлам. Я рекомендую прочитать статьи в http://make.mad-scientist.net/papers/.

Вы также можете рассмотреть возможность использования генератора Makefile, например cmake.

Если вы скопируете и вставите код Makefile отсюда, обязательно замените пробелы в начале строк на вкладку.

...