GNU Makefile - универсальный Makefile для нескольких целей

В настоящее время я разрабатываю небольшой пакет C++ с небольшими примерами кода для учебных целей. Мне удалось написать Makefile, подобный следующему, который скомпилирует все файлы *.cpp в файлы *.o и свяжет их с исполняемыми файлами:

CC=gcc
CFLAGS=-g
LDFLAGS= -lstdc++
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#a generic rule to create .o files from .cpp files (e.g. example1.cpp -> example1.o)
%.o: %.cpp
    $(SILENT) $(ECHO) "--- Compiling $< ---"
    $(SILENT) $(CC) -c $(CFLAGS) $<

#define targets and their dependencies
example1: example1.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example2: example2.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

example3: example3.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

Пока все хорошо, это работает хорошо. Он возьмет example1.cpp, example2.cpp и example3.cpp и скомпилирует / свяжет его с 3 исполняемыми файлами "example1 example2 example3".

Но так как каждый исполняемый файл имеет то же имя, что и объект (например, "example1.o" будет связан с исполняемым файлом "example1"), мне интересно, есть ли способ использовать также общее правило.

Я пробовал несколько вещей, таких как:

%: %.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ -o $@ $(LDFLAGS)

Насколько я понимаю, это правило должно брать все объектные файлы и создавать исполняемый файл с тем же именем, что и объектный файл, но я не мог заставить его работать! У кого-нибудь есть подсказка, как этого добиться?

2 ответа

Решение

Вот решение:

В приведенных выше примерах я не использую переменную $(LDLIBS), которая используется встроенным правилом по умолчанию. Если я изменю свой Makefile на использование $(LDLIBS) вместо $(LDFLAGS), все будет хорошо!

Решение № 1: (встроенное правило)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS= 
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

Решение № 2: (пользовательские правила)

CC=gcc
CFLAGS=-g -fopenmp
LDFLAGS= 
LDLIBS = -lstdc++ -lgomp
ECHO = echo
SILENT = @

MODULES = example1 example2 example3

all: $(MODULES)

#disable built-in rule
%: %.cpp

#rule for *.cpp -> *.o
%.o: %.cpp
    $(SILENT) $(ECHO) "--- Compiling $< ---"
    $(SILENT) $(CC) -c $(CFLAGS) $(LDFLAGS) $<

#rule for object -> exectutable
%: %.o
    $(SILENT) $(ECHO) "--- Linking $@ ---"
    $(SILENT) $(CC) $^ $(LDLIBS) -o $@ 

clean:
    $(SILENT) $(ECHO) "--- Removing object files and binaries ---"
    $(SILENT) rm -f *.o 
    $(SILENT) rm -f $(MODULES)

.PHONY: clean

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

Вы можете отменить правило, переопределив его пустым правилом:

%: %.cpp

Или вы можете полностью удалить свои правила и позволить этому правилу делать правильные вещи. Неявное правило имеет рецепт в соответствии с

$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@

так что вы можете использовать эти переменные, чтобы выбрать компилятор и указать флаги для препроцессора, компилятора и компоновщика. Например, вы можете изменить CXX использовать компилятор для неправильного языка и исправить ущерб, добавив LDFLAGS=-lstdc++, как ваш make-файл, если вы действительно хотите.

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