Как отметил Джон Боллинджер в комментариях, вам, вероятно, следует явно указать либо источники, либо объектные файлы.Не делать это менее безопасно, менее предсказуемо, а цена, которую вы платите за сложность 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
Запуск всего make-файла приводит к следующей ошибке:
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 ++, ассемблера, fortran и т. Д. Для C ++ команда выглядит так:
COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
Поскольку ваши пользователи могут настраивать эти флагиТем не менее, убедитесь, что ваши обязательные каталоги include
не находятся в CPPFLAGS
.Эта переменная предназначена для каталогов включения дополнительных библиотек, если ваше приложение позволяет пользователям компилировать с дополнительными функциями, если у них установлена конкретная библиотека.
Команда компоновщика: LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
, где LDFLAGS
- компоновщикпеременная, которую ваши пользователи также ожидают настроить.
Все это действительно начинает иметь значение, когда ваши проекты начинают набирать размер и сложность.В больших проектах обычно есть вложенные подпапки, а make-файл верхнего уровня будет рекурсивно вызывать каждый вложенный make-файл, и именно тогда соблюдение четкого, четко определенного соглашения начинает действительно иметь значение.
Написание хороших make-файлов требует практики, но это может быть очень полезным. Я видел несколько действительно мастерских, ни один из них, к сожалению, не написан мной. Надеюсь, это поможет несколько, хотя; для меня всегда полезно знать, почему все делается так, как есть, с технической точки зрения, прежде чем я действительно понимаю и принимаю принятые практики.