Создайте исполняемый файл, используя шаблон в Makefile

Вот простой 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

Не могли бы вы объяснить немного подробнее, что происходит и как решить эту небольшую проблему?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
608
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

%.o не является подстановочным знаком оболочки, он работает только в контексте правила шаблона. Чтобы делать то, что вы хотите, вы должны использовать Функция подстановочный знак, например.

 main: $(wildcard *.o)
           $(CXXCOMP) $(CXXFLAGS) -o $@ $^

но, как правильно указывает Мэтт, имеет ряд различных проблем. Лучше использовать переменную сделать для исходных файлов, а затем составить список объектных файлов.

SRC = $(wildcard *.cpp)
OBJ = $(SRC:.cpp=.o)

main: $(OBJ)
      ...

... или явно назвать все источники или объекты, что является более безопасным и традиционным, и даже может позволить себе make-файл, который не зависит от расширений GNU.

John Bollinger 29.05.2019 13:59

Является ли wildcard командой bash или это функция GNU make only? Я спрашиваю об этом, потому что make печатает clang ошибку, теперь: no input files. Другими словами, должен ли я установить что-то еще, чтобы использовать wildcard?

rudicangiotti 29.05.2019 14:06

Как отметил Джон Боллинджер в комментариях, вам, вероятно, следует явно указать либо источники, либо объектные файлы. Не делать этого менее безопасно, менее предсказуемо, и цена, которую вы платите за сложность 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)?

rudicangiotti 29.05.2019 16:53
CXXFLAGS предназначен для флагов, специфичных для компиляции C++, а CPPFLAGS - специально для параметров препроцессора (это то, что означает PP, а не «C Plus Plus», как некоторые могут подумать). Он также используется в сборках C по умолчанию, что вы также можете увидеть с помощью make --print-data-base | grep -E 'COMPILE' под COMPILE.c.
Jose Fernando Lopez Fernandez 29.05.2019 18:18
If you set OBJS to *.o and echo the variable, you get this На самом деле, это плохой пример, так как любая оболочка POSIX/echo буду автоматически расширяет подстановочные знаки.
Matt 29.05.2019 18:33

Я заранее проверил это (я на manjaro linux) и make точно повторил echo *.o, но я признаю, что не совсем уверен, почему bash не интерпретировал подстановочный знак как таковой.

Jose Fernando Lopez Fernandez 29.05.2019 18:43

Другие вопросы по теме