Как заставить Makefile перекомпилироваться при изменении заголовочного файла? - PullRequest
0 голосов
/ 27 августа 2018

Я написал Makefile для компиляции программы openCV в OSX (более общий в системах Unix).

Код имеет заголовок с именем constants.hpp, в котором определены некоторые константы.

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

Мой Makefile следующий

CPP = g++
CPPFLAGS = -std=c++11

all: main.o

main.o: main.cpp
    $(CPP) $^ $(CPPFLAGS) -o $@

Поиск вокругЯ попытался определить после CPPFLAGS значение:

DEPS = constants.hpp 

, а затем, поскольку от него зависит main.o, добавив зависимость следующим образом:

main.o: main.cpp $(DEPS)
        $(CPP) $^ $(CPPFLAGS) -o $@

, но ошибка, которую я получаю,clang: error: cannot specify -o when generating multiple output files.

Я также пытался этот ответ и пытался использовать флаги M MM, но мне чего-то не хватает.

Как сделать Makefileперекомпилировать при изменении заголовочного файла?

РЕДАКТИРОВАТЬ: После комментариев к DevSolar мне пришлось полностью изменить вопрос, и он задал также исходный код.Так как я нашел здесь бесполезным копировать весь исходный код, я упростил его с помощью простой программы hello world.

Ниже приводится main.cpp:

#include<iostream>
#include"constants.hpp"

int main()
{

  std::cout<<"Hello world, the value is: " <<  myValue <<"\n";
  return 0;
}

и следующие constants.hpp:

static const int myValue = 10;

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Предисловие

Вы используете $(CPP) и $(CPPFLAGS) ... это для препроцессора .То, что вы хотите использовать, это $(CXX) и $(CXXFLAGS) для компилятора C ++.

В следующем предполагается, что GNU make и GCC-совместимый компилятор (clang сделает).

Шаг первый

Используйте общие правила, а не одно на исходный файл - последний очень быстро станет громоздким и очень подвержен ошибкам.

Либо перечислите свои исходные файлы вручную...

SOURCES := parking.cpp utils.cpp InputStateContext.cpp InputState.cpp InputStateA.cpp

... или автоматически перечислить все исходные файлы:

SOURCES := $(wildcard *.cpp)

Вам также необходим список объектных файлов (по одному .o на .cpp):

OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))

Теперь предоставьте правило для исполняемого файла, в зависимости от всех объектных файлов ...

parking: $(OBJECTS)
        $(CXX) $(CXXFLAGS) $^ -o $@

... и универсальное правило о том, какчтобы превратить отдельный исходный файл в объектный файл:

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) &< -o $@

Зависимость этого правила также от Makefile гарантирует, что, например, изменения в $(CXXFLAGS) также инициируют перекомпиляцию.$< разрешает зависимость first (исходный файл).

Мы расширим это правило позже.

Шаг второй

У нас будет файл зависимости для каждого исходного файла .(Смирись со мной здесь.) Мы можем создать список этих файлов ...

DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))

... и включить их в Makefile:

-include $(DEPENDS)

-там означает, что make не будет жаловаться, если эти файлы не существуют - потому что их нет, на данный момент.

Шаг третий (Основная часть ответа на ваш вопрос)

Пусть компилятор сгенерирует эти файлы зависимостей для us - потому что он знает лучше.Для этого мы расширим наше правило сборки:

%.o: %.cpp Makefile
        $(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@

Флаг -MMD создает файл зависимостей (%.d), который будет содержать (в синтаксисе Makefile) правила, создающие сгенерированный файл (%.o)в этом случае) зависит от исходного файла и любых несистемных заголовков, в которые он входит .Это означает, что объектный файл воссоздается автоматически при каждом касании соответствующих источников.Если вы также хотите зависеть от системных заголовков (т. Е. Проверять их наличие обновлений при каждой компиляции), используйте вместо этого -MD.

Опция -MP добавляет пустые фиктивные правила, которые позволяют избежать ошибок при удалении заголовочных файлов из файловой системы.

При первом запуске компиляции информация о зависимостях отсутствует - но посколькуобъектные файлы тоже не существуют, компилятор должен работать в любом случае.Для каждого последующего запуска make будет включать автоматически созданные файлы зависимостей и «делать правильные вещи».


All-In-One (с некоторыми более синтаксическимисахар добавлен):

SOURCES := $(wildcard *.cpp)
OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES))
DEPENDS := $(patsubst %.cpp,%.d,$(SOURCES))

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

# .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: parking

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

# Linking the executable from the object files
parking: $(OBJECTS)
        $(CXX) $(WARNING) $(CXXFLAGS) $^ -o $@

-include $(DEPENDS)

%.o: %.cpp Makefile
        $(CXX) $(WARNING) $(CXXFLAGS) -MMD -MP -c $< -o $@
0 голосов
/ 27 августа 2018

Если у вас есть только один файл исходного кода, входящий в компиляцию, от которой зависит объектный файл, попробуйте

main.o: main.cpp $(DEPS)
      $(CPP) -c $< $(CPPFLAGS) -o $@

Это будет работать только в том случае, если вы не используете несколько .cpp файлов, входящих в компиляцию.

$^ - это автоматическая переменная, которая содержит все предпосылки текущего рецепта, разделенные пробелами, тогда как $< просто содержит первую предпосылку. Вот почему вы не можете использовать это решение для нескольких .cpp файлов в одной и той же компиляции.

...