Make: неявное правило для ссылки на проект С++

Я работаю над учебником. Очень простые тестовые проекты, которые я пытаюсь создать, имеют только 3 файла: ./src/main.cpp./src/implementation.cpp и ./include/header.hpp Это файл make.

VPATH = src include
CPPFLAGS = -I include

main: main.o implementation.o
main.o: header.hpp
implementation.o: header.hpp

При вызове без каких-либо аргументов make строит только объектные файлы, но не связывает исполняемый файл. Предполагалось, что это неявное правило для prog или я чего-то не хватает? Мне действительно нужно, чтобы кто-то указал мне в правильном направлении.

Спасибо.

Я сделал первое имя цели таким же, как префикс исходного файла. Теперь вызовите cc, чтобы связать файлы объектов.

g++  -I include  -c -o main.o src/main.cpp    
g++  -I include  -c -o implementation.o src/implementation.cpp
cc   main.o implementation.o   -o main

По какой-то причине ссылка на g++ работает, но ссылка на cc не работает.

Я могу явно указать правило, но хочу узнать, как использовать неявные правила.

Ответы

Ответ 1

В соответствии с make manual вы можете использовать правило неявного связывания с несколькими объектами, если одно из них соответствует исполняемому имени, например:

VPATH = src include
CPPFLAGS = -I include

main: implementation.o
main.o: header.hpp
implementation.o: header.hpp

Это создаст исполняемый файл named main из main.o и implementation.o.

Обратите внимание, однако, что встроенное неявное правило использует компилятор C для связывания, который по умолчанию не связывается с библиотекой std С++, вам нужно явно указать это в LDFLAGS

Ответ 2

Как насчет этого для минимального Makefile:

SOURCES = src/main.cpp src/implementation.cpp

CXX = g++
CXXFLAGS = -g -W -Wall -Werror
LDFLAGS = -g

OBJECTS = $(SOURCES:.cpp=.o)

prog: $(OBJECTS)
    $(CXX) $(LDFLAGS) -o [email protected] $^

clean::
    $(RM) prog

.cpp.o:
    $(CXX) -MD -MP $(CXXFLAGS) -o [email protected] -c $<

clean::
    $(RM) src/*.o

DEPENDS = $(SOURCES:.cpp=.d)

-include $(DEPENDS)

%.d:
    @touch [email protected]

clean::
    $(RM) src/*.d

Это предполагает GNU make и gcc, но добавляет правильное отслеживание зависимостей, поэтому нет необходимости явно перечислять зависимости файлов заголовков.

Ответ 3

Там должно быть неявное правило для prog или я что-то не хватает?

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

Ответ 4

Мой ответ (аккуратное решение): добавьте

LDLIBS = -lstdc++

в Makefile. В соответствии с руководство:

Библиотеки (-lfoo) следует добавить вместо переменной LDLIBS.

LDFLAGS предназначены для флагов каталога библиотеки, таких как -L. На моем Linux (Ubuntu 14.04) LDFLAGS = -lstdc++ создает

cc -lstdc++ main.o -o main

который не работает, а LDLIBS = -lstdc++ производит

cc main.o -lstdc++ -o main

который работает. Я понятия не имею, почему порядок имеет значение, но он имеет смысл в соответствии с ролями двух встроенных переменных. Мне не нравится псевдоним CC to CXX, который кажется взломанным, а также запутанным для читателей.

LDFLAGS v.s. LDLIBS

Я просто попытался и обнаружил, что этот компилятор не работает для LDFLAGS = -lstdc++:

  • GCC 4.8.5 на Ubuntu 14.04

но следующие компиляторы принимают LDFLAGS = -lstdc++:

  • Clang 3.4 на Ubuntu 14.04
  • GCC 4.7.2 на Debian Wheezy
  • Clang 7.3.0 (703.0.31) на OS X

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

Почему другие решения выше?

  • Хастуркун: имеющий main.o в правиле

    main: main.o implementation.o
    

    не приводит к ошибке. main.o не требуется, но он все равно сделает.

  • Саймон Рихтер: решение устраняет необходимость отслеживать заголовок (что действительно здорово), но OP хочет неявное правило. Мы действительно можем получить лучшее из обоих:

    VPATH = src include
    CPPFLAGS = -I include -MMD -MP
    CXXFLAGS = -g -W -Wall -Werror
    LDLIBS = -lstdc++
    
    target = main
    lib = implementation.cpp
    objects = $(target:=.o) $(lib:.cpp=.o)
    
    $(target): $(objects)
    %.o: %.cpp %.d
    %.d: ;
    
    -include $(objects:.o=.d)
    
    clean::
        $(RM) $(target) $(objects) $(objects:.o=.d)
    

    который делает то, что он сделал, а также использует неявные правила. Однако этого полного решения не должно быть здесь, поскольку это не то, чего хочет OP: OP просто хочет знать, почему его Makefile не работает и почему. "Auto-Dependency Generation" - еще одна огромная тема, заслуживающая полного учебника (например, этот). Так что я сожалею, но его ответ фактически не ответил на вопрос.

  • Jerome: Я использовал для изменения LINK.o от $(CC) $(LDFLAGS) $(TARGET_ARCH) до $(CXX) $(LDFLAGS) $(TARGET_ARCH), и он работал, но я предпочитаю редактировать LDLIBS, поскольку LINK.o не документирован (и, возможно, не является частью общедоступного API), а не в будущем. Я бы назвал это хаком.

Ответ 5

Хм...

CXX = g++               # Just an example
CXXFLAGS = -Wall -O2    # Just an example

prog: main.o implementation.o
    $(CXX) $(CXXFLAGS) $^ -o [email protected]

main.o: header.hpp
implementation.o: header.hpp

должен выполнить эту работу. Почему ваш вариант не работает, объясняется в Конрад Рудольф ответ

Ответ 6

Неявное правило для связывания объектных файлов:

$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o [email protected]

Но $(LINK.o) использует $(CC), что не совсем корректно для компиляции программ на С++, поэтому самое легкое исправление:

CC = $(CXX)