Makefile выполнил неожиданное поведение - PullRequest
0 голосов
/ 13 ноября 2018

Я использовал следующий Makefile для компиляции файлов C ++, но произошло непредвиденное поведение. Средой является MacOS X Mojave. Makefile выглядит так:

CC=gcc
CXX=g++
CXXFLAGS=-std=c++11
RM=rm -f

all: clean sort_test ds_test

sort_test: data_structure sort .sort_test.o .sort_test

.sort_test: sort_test.o sort.o ds.o
        $(CXX) $(CXXFLAGS) -o sort_test sort_test.o sort.o ds.o

.sort_test.o: sort_test.cpp ../include/io.hpp
        $(CXX) $(CXXFLAGS) -c -o sort_test.o sort_test.cpp

sort: ../include/sort.hpp ../include/data_structure.hpp ../src/sort.cpp
        $(CXX) $(CXXFLAGS) -c -o sort.o ../src/sort.cpp

data_structure: ../include/data_structure.hpp ../src/data_structure.cpp ../include/io.hpp
        $(CXX) $(CXXFLAGS) -c -o ds.o ../src/data_structure.cpp

ds_test: data_structure .ds_test.o .ds_test

.ds_test: ds.o ds_test.o
        $(CXX) $(CXXFLAGS) -o ds_test ds.o ds_test.o

.ds_test.o: ds_test.cpp ../include/io.hpp ../include/data_structure.hpp
        $(CXX) $(CXXFLAGS) -c -o ds_test.o ds_test.cpp

clean:
        $(RM) *.o sort_test ds_test

Когда я запускаю "make ds_test" в той же директории, происходит что-то странное:

g++ -std=c++11   -c -o ds_test.o ds_test.cpp
g++ -std=c++11 -c -o ds.o ../src/data_structure.cpp
g++ -std=c++11 -c -o ds_test.o ds_test.cpp
g++ -std=c++11 -o ds_test ds.o ds_test.o
gcc   ds_test.o data_structure .ds_test.o .ds_test   -o ds_test
clang: error: no such file or directory: 'data_structure'
clang: error: no such file or directory: '.ds_test.o'
clang: error: no such file or directory: '.ds_test'
make: *** [ds_test] Error 1

Первая и пятая строки в этих выходных данных никогда не ожидаются для команды "make ds_test", поскольку она должна вызывать только "data_structure", ".ds_test.o" и ".ds_test". Кто-нибудь, пожалуйста, объясните, почему могло произойти такое непредвиденное поведение и как его избежать? Спасибо!

1 Ответ

0 голосов
/ 13 ноября 2018

Ваш Makefile немного странный. Основные правила make выглядят примерно так:

file-to-build: files-it-depends-on
    command-to-build

Пока вы писали такие вещи, как:

.ds_test: ds.o ds_test.o
        $(CXX) $(CXXFLAGS) -o ds_test ds.o ds_test.o

где целью является не файл, созданный по рецепту. Более того, вы переименовываете вещи без правильных расширений (data_structure против ds.o). И, наконец, вы используете разные имена для одной и той же вещи (снова data_structure против ds.o). Если вы начинаете с C ++ и делаете make, вам, вероятно, следует избегать всех этих причудливых вещей.

Основная причина сбоя вашего Makefile в том, что make пытается создать файл с именем ds_test (это то, что вы запрашиваете при вводе make ds_test). И make знает много способов для создания файлов. В этом конкретном случае он использует свое правило по умолчанию, которое заключается в использовании $(CC) для связи вместе ds_test.o, а все остальные файлы ds_test зависят от, то есть data_structure, .ds_test.o и .ds_test.

Если вы новичок в создании, я предлагаю вам сначала придерживаться его самых фундаментальных принципов. Что-то вроде:

CC       := gcc
CXX      := g++
CXXFLAGS := -std=c++11
RM       := rm -f

.PHONY: all clean

all: clean sort_test ds_test

sort_test: sort_test.o sort.o data_structure.o
    $(CXX) $(CXXFLAGS) -o $@ $^

sort_test.o: sort_test.cpp ../include/io.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

sort.o: ../src/sort.cpp ../include/sort.hpp ../include/data_structure.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

data_structure.o: ../src/data_structure.cpp ../include/data_structure.hpp ../include/io.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

ds_test: data_structure.o ds_test.o
    $(CXX) $(CXXFLAGS) -o $@ $^

ds_test.o: ds_test.cpp ../include/io.hpp ../include/data_structure.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

clean:
    $(RM) *.o sort_test ds_test

Пояснения:

  • $@, $< и $^ - это , создающие автоматические переменные , которые соответственно расширяются как цель, первая предпосылка и список всех предпосылок. Они не просто удобны, они намного менее подвержены ошибкам, чем перепечатывание одинаковых имен файлов в целях, предпосылках и рецептах.
  • .PHONY - это специальная цель , с помощью которой вы указываете, что цели не являются реальными файлами.

EDIT : добавлено LDLIBS для связывания объектных файлов C ++ с gcc и альтернативным правилом связывания.

Примечание: поскольку make действительно умный и по умолчанию знает, как компилировать и связывать файлы C ++, вы можете упростить все это. Особенно если вы также используете VPATH make variable :

CC       := gcc
CXX      := g++
CXXFLAGS := -std=c++11
LDLIBS   := -lstdc++
RM       := rm -f
EXEC     := sort_test ds_test

.PHONY: all clean

all: clean sort_test ds_test

VPATH    := ../src:../include

sort_test.o: io.hpp
sort.o: sort.hpp data_structure.hpp
data_structure.o: data_structure.hpp io.hpp
ds_test.o: io.hpp data_structure.hpp

sort_test: sort_test.o sort.o data_structure.o
ds_test: data_structure.o ds_test.o

clean:
    $(RM) *.o $(EXEC)

Примечание: поскольку make будет использовать gcc для связи, мы должны добавить -lstdc++ к флагам компоновщика (LDLIBS). Другой вариант - указать правило связывания вместо того, чтобы позволить make использовать значение по умолчанию:

$(EXEC):
    $(CXX) $(CXXFLAGS) -o $@ $^

Обратите внимание, что в этом последнем случае правило, указывающее предварительные условия, и правило, указывающее рецепт, различаются.

...