Вот простой Makefile
контент:
CXXCOMP=g++
CXXFLAGS=-std=c++11 -g
%.o: %.cpp
$(CXXCOMP) $(CXXFLAGS) -c $<
main: %.o
$(CXXCOMP) $(CXXFLAGS) -o $@ $^
Я ожидаю, что первое правило создаст файл .o
для каждого .cpp
, а второе правило создаст файл main
из всех .o
.
На самом деле, когда я пытаюсь сделать main
, на терминал выводится следующий оператор:
target `main' doesn't match the target pattern
Не могли бы вы объяснить немного подробнее, что происходит и как решить эту небольшую проблему?
%.o
не является подстановочным знаком оболочки, он работает только в контексте правила шаблона. Чтобы делать то, что вы хотите, вы должны использовать Функция подстановочный знак, например.
main: $(wildcard *.o)
$(CXXCOMP) $(CXXFLAGS) -o $@ $^
но, как правильно указывает Мэтт, имеет ряд различных проблем. Лучше использовать переменную сделать для исходных файлов, а затем составить список объектных файлов.
SRC = $(wildcard *.cpp)
OBJ = $(SRC:.cpp=.o)
main: $(OBJ)
...
Является ли wildcard
командой bash или это функция GNU make only? Я спрашиваю об этом, потому что make
печатает clang
ошибку, теперь: no input files
. Другими словами, должен ли я установить что-то еще, чтобы использовать wildcard
?
Как отметил Джон Боллинджер в комментариях, вам, вероятно, следует явно указать либо источники, либо объектные файлы. Не делать этого менее безопасно, менее предсказуемо, и цена, которую вы платите за сложность make-файла, даже не стоит того.
Чтобы обойти сложность, вам нужно было бы иметь все ваши заголовочные файлы, исходные файлы, объектные файлы и выходные файлы в одной папке, что не очень хорошо. Однако это позволит вам просто использовать функцию $(wildcard *.cpp)
. В противном случае вам пришлось бы связать серию вызовов, чтобы сначала получить все исходные файлы в каталоге src
, использовать функцию notdir
, чтобы удалить часть имени каталога, а затем использовать функцию patsubst
, чтобы выполнить переключение в расширении имени файла. . Это будет выглядеть примерно так:
OBJS = $(patsubst %.cpp, %.o, $(notdir $(wildcard src/*.cpp)))
Лично я предпочитаю явно перечислять все объектные файлы, но это сделает то, что вы хотите.
Кроме того, причина, по которой вам нужна функция wildcard
, заключается в том, что, хотя подстановочные знаки обычно работают так, как ожидается в правилах и целях, вы используете их в присваивании переменных, где они воспринимаются буквально. Если вы установите OBJS
на *.o
и повторите переменную, вы получите следующее:
OBJS = *.o
echo_objs:
@echo $(OBS)
Выход:
echo *.o
Запуск всего makefile дает следующую ошибку:
make: *** No rule to make target '*.o', needed by 'project'. Stop.
В настоящее время в каталоге нет объектных файлов, поэтому использование подстановочной функции в присваивании приводит к тому, что ничего не выводится, что является обычным поведением. Я повторил имена всех объектных файлов в каталоге: нет.
Что мне нравится делать, так это перечислять объектные файлы следующим образом:
OBJS = main.o file.o string.o # or whatever
Затем, предварительно установив переменную vpath
следующим образом:
vpath %.cpp src
vpath %.hpp include
Я могу просто написать:
OBJS = main.o
ASMS = $(patsubst %.o, %.asm, $(OBJS))
TARGET = project_name
all: assembler_output $(TARGET) # etc
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -o $@ $^ $(LDFLAGS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -c -o $@ $^
assembler_output: $(ASMS)
%.asm: %.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -masm=intel -S -o $@ $^
.PHONY: clean
clean:
$(RM) $(OBJS) $(TARGET)
Я включил дополнительную цель, потому что: а) мне нравится видеть, что делает компилятор (а иногда и препроцессор) до вызова компоновщика, и б) надеюсь, что это поможет несколько разбить ваши цели на модули.
Надеюсь, я ответил на главный вопрос, и у меня есть несколько предложений по вашему make-файлу. Вы действительно должны придерживаться соглашения об именах переменных компилятора C CC
и компилятора C++ CXX
, особенно если вы хотите опубликовать свой код для использования другими. Ваши пользователи будут ожидать, что смогут настроить параметры компиляции для проекта при его сборке (возможно, у них нет или они не используют g++), и они будут удивлены, когда будут продолжать изменять переменную CXX
безрезультатно. .
Хотя make-файл для игрушечного проекта короткий, он может довольно быстро стать довольно громоздким, особенно с тестовым кодом, директивами установки и т. д., поэтому заставить ваших пользователей cat -n ./makefile | grep -E 'CXX'
для всех возможных директив компиляции выследить реальную переменную будет довольно неплохо. раздражающий. Не говоря уже о том, что вы объявляете переменную в make-файлом, поэтому они не могут переопределить настройки. Возможно, вы захотите использовать ?=
для этих переменных, таким образом, они определяются только в том случае, если никакие настройки не были установлены через консоль.
На самом деле, если вы запустите make --print-data-base
в каталоге проекта и grep -E 'COMPILE'
, вы увидите точные встроенные команды make
для компиляции C, C++, ассемблера, фортрана и т. д. Для C++ это команда:
COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
Однако, поскольку ваши пользователи могут настраивать эти флаги, убедитесь, что ваши обязательные include
каталоги не находятся в CPPFLAGS
. Эта переменная предназначена для каталогов включения дополнительных библиотек, если ваше приложение позволяет пользователям компилировать дополнительные функции, если у них установлена определенная библиотека.
Команда компоновщика — LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
, где LDFLAGS
— переменная компоновщика, которую ваши пользователи также могут настроить.
Все это действительно начинает иметь значение, когда ваши проекты начинают увеличиваться в размерах и усложняться. Большие проекты обычно имеют вложенные подпапки, а make-файл верхнего уровня будет рекурсивно вызывать каждый вложенный make-файл, и именно тогда соблюдение четкого, четко определенного соглашения начинает действительно иметь значение.
Написание хороших make-файлов требует практики, но это может быть очень полезным. Я видел несколько действительно мастерских, но, к сожалению, ни один из них не был написан мной. Надеюсь, это немного поможет; для меня всегда полезно знать, почему вещи делаются такими, какие они есть, с технической точки зрения, прежде чем я действительно пойму и приму общепринятые методы.
Учитывая соглашения, которые вы упомянули, в чем разница между $(CXXFLAGS)
и $(CPPFLAGS)
?
CXXFLAGS
предназначен для флагов, специфичных для компиляции C++, а CPPFLAGS
- специально для параметров препроцессора (это то, что означает PP, а не «C Plus Plus», как некоторые могут подумать). Он также используется в сборках C по умолчанию, что вы также можете увидеть с помощью make --print-data-base | grep -E 'COMPILE'
под COMPILE.c
.
If you set OBJS to *.o and echo the variable, you get this
На самом деле, это плохой пример, так как любая оболочка POSIX/echo буду автоматически расширяет подстановочные знаки.
Я заранее проверил это (я на manjaro linux) и make
точно повторил echo *.o
, но я признаю, что не совсем уверен, почему bash не интерпретировал подстановочный знак как таковой.
... или явно назвать все источники или объекты, что является более безопасным и традиционным, и даже может позволить себе make-файл, который не зависит от расширений GNU.