Создайте исполняемый файл, используя шаблон в Makefile - PullRequest
1 голос
/ 29 мая 2019

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

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

Ответы [ 2 ]

2 голосов

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

2 голосов
/ 29 мая 2019

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

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

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

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

main: $(OBJ)
      ...
...