Нужна помощь с раздражающими ошибками Makefile g++: g++ и ошибки оболочки, а также советы по проектированию нескольких файлов Makefile

У меня есть make-файл:

#Nice, wonderful makefile written by Jason
CC=g++
CFLAGS=-c -Wall
BASE_DIR:=.
SOURCE_DIR:=$(BASE_DIR)/source
BUILD_DIR:=$(BASE_DIR)/build
TEST_DIR:=$(BASE_DIR)/build/tests
MAKEFILE_DIR:=$(BASE_DIR)/makefiles
DATA_DIR:=$(BASE_DIR)/data
DATA_DIR_TESTS:=$(DATA_DIR)/tests
MOLECULE_UT_SOURCES :=  $(SOURCE_DIR)/molecule_test/main.cc \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager.cpp \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_manager_main.cpp \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader.cpp \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.cpp \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager.cpp \
    $(SOURCE_DIR)/parser.h \
    $(SOURCE_DIR)/parser.cpp \
    $(SOURCE_DIR)/common.h
MOLECULE_UT_DATA := \
    $(DATA_DIR_TESTS)/molecule_test/par_oxalate_and_friends.inp \
    $(DATA_DIR_TESTS)/molecule_test/dicarboxy-octane_4.pdb \
    $(DATA_DIR_TESTS)/molecule_test/dicarboxy-octane_4.psf
PARAM_UT_SOURCES :=  $(SOURCE_DIR)/parameter_test/main.cc \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager.cpp \
    $(SOURCE_DIR)/parser.h \
    $(SOURCE_DIR)/parser.cpp \
    $(SOURCE_DIR)/common.h
PARAM_UT_DATA := $(DATA_DIR_TESTS)/molecule_test/par_oxalate_and_friends.inp

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
    molecule_test_prepare_data_files
    @$(shell cd $(TEST_DIR)/molecule_unit_test/; \
    make ./bin/molecule_test)

molecule_test_prepare_sources: molecule_test_dir
    @echo Copying sources...
    @cp --preserve $(MOLECULE_UT_SOURCES) \
    $(TEST_DIR)/molecule_unit_test/source

molecule_test_prepare_makefiles: $(MAKEFILE_DIR)/Makefile.molecule_test
    @cp  --preserve $(MAKEFILE_DIR)/Makefile.molecule_test \
    $(TEST_DIR)/molecule_unit_test/Makefile

molecule_test_prepare_data_files:
    cp --preserve $(MOLECULE_UT_DATA) $(TEST_DIR)/molecule_unit_test/bin/

molecule_test_dir:
    @if test -d $(BUILD_DIR); then \
        echo Build exists...; \
        else \
        echo Build directory does not exist, making build dir...; \
    mkdir $(BUILD_DIR); \
        fi
    @if test -d $(TEST_DIR); then \
        echo Tests exists...; \
        else \
        echo Tests directory does not exist, making tests dir...; \
    mkdir $(TEST_DIR); \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test; then \
        echo Molecule unit test directory exists...; \
        else \
        echo Molecule unit test directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/source; then \
        echo Molecule unit test source directory exists...; \
        else \
        echo Molecule unit test source directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/source; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/obj; then \
        echo Molecule unit test object directory exists...; \
        else \
        echo Molecule unit test object directory does \
        not exist, making object dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/obj; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/bin; then \
        echo Molecule unit test executable directory exists...; \
        else \
        echo Molecule unit test executable directory does \
        not exist, making executable dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/bin; \
        fi

param_test : param_test_prepare_sources param_test_prepare_makefiles \
    param_test_prepare_data_files
    @$(shell cd $(TEST_DIR)/param_unit_test/; \
    make ./bin/param_test)

param_test_prepare_sources: param_test_dir
    @echo Copying sources...
    @cp --preserve $(PARAM_UT_SOURCES) $(TEST_DIR)/param_unit_test/source

param_test_prepare_makefiles: $(MAKEFILE_DIR)/Makefile.param_test
    @cp  --preserve $(MAKEFILE_DIR)/Makefile.param_test \
    $(TEST_DIR)/param_unit_test/Makefile

param_test_prepare_data_files:
    cp --preserve $(PARAM_UT_DATA) $(TEST_DIR)/param_unit_test/bin/

param_test_dir:
    @if test -d $(BUILD_DIR); then \
        echo Build exists...; \
        else \
        echo Build directory does not exist, making build dir...; \
    mkdir $(BUILD_DIR); \
        fi
    @if test -d $(TEST_DIR); then \
        echo Tests exists...; \
        else \
        echo Tests directory does not exist, making tests dir...; \
    mkdir $(TEST_DIR); \
        fi
    @if test -d $(TEST_DIR)/param_unit_test; then \
        echo Param unit test directory exists...; \
        else \
        echo Param unit test directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/param_unit_test; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/source; then \
        echo Param unit test source directory exists...; \
        else \
        echo Param unit test source directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/param_unit_test/source; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/obj; then \
        echo Param unit test object directory exists...; \
        else \
        echo Param unit test object directory does \
        not exist, making object dir...; \
        mkdir $(TEST_DIR)/param_unit_test/obj; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/bin; then \
        echo Param unit test executable directory exists...; \
        else \
        echo Param unit test executable directory does \
        not exist, making executable dir...; \
        mkdir $(TEST_DIR)/param_unit_test/bin; \
        fi

Это вызывает второй make-файл после того, как он создаст и заполнит структуру каталогов.

Второй make-файл выглядит следующим образом:

#Nice, wonderful makefile written by Jason
CC=g++
CFLAGS=-c -Wall
SOURCE_DIR:=./source
OBJ_DIR:=./obj
EXE_DIR:=./bin

$(EXE_DIR)/molecule_test : $(OBJ_DIR)/main.o \
    $(OBJ_DIR)/parameter_manager_lj_molecule.o \
    $(OBJ_DIR)/parameter_manager.o $(OBJ_DIR)/parser.o \
    $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
    $(OBJ_DIR)/molecule_reader.o \
    $(OBJ_DIR)/molecule_reader_psf_pdb.o
    @$(CC) $(OBJ_DIR)/main.o $(OBJ_DIR)/parameter_manager.o \
    $(OBJ_DIR)/parser.o $(OBJ_DIR)/parameter_manager_lj_molecule.o \
    $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
    $(OBJ_DIR)/molecule_reader.o \
    $(OBJ_DIR)/molecule_reader_psf_pdb.o \
    -o molecule_test
    @mv molecule_test $(EXE_DIR)/ 

$(OBJ_DIR)/main.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/common.h $(SOURCE_DIR)/main.cc
    $(CC) $(CFLAGS) $(SOURCE_DIR)/main.cc
    @mv main.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_reader.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_reader.cpp
    @mv molecule_reader.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_reader_psf_pdb.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_reader_psf_pdb.cpp
    @mv molecule_reader_psf_pdb.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_manager.o: $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_manager.cpp
    @mv molecule_manager.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_manager_main.o: $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_manager_main.cpp
    @mv molecule_manager_main.o $(OBJ_DIR)/

$(OBJ_DIR)/parameter_manager_lj_molecule.o: $(SOURCE_DIR)/common.h \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parser.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp
    @mv parameter_manager_lj_molecule.o $(OBJ_DIR)/

$(OBJ_DIR)/parameter_manager.o: $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/parameter_manager.cpp
    @mv parameter_manager.o $(OBJ_DIR)/

$(OBJ_DIR)/parser.o: $(SOURCE_DIR)/parser.h
    @$(CC) $(CFLAGS) $(SOURCE_DIR)/parser.cpp
    @mv parser.o $(OBJ_DIR)/

$(OBJ_DIR)/common.o: $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/common.h
    mv common.h.gch $(OBJ_DIR)/

Я признаю, что я немного новичок в Makefiles. Я хотел бы получить совет, как упростить эти файлы (без лишней «магии») и как исправить эти две ошибки...

Во-первых, я должен сказать, что все работает, так сказать. Когда я создаю свою цель, она создает все каталоги правильно и генерирует исполняемый файл. И все мои файлы правильно копируются и перекомпилируются, когда я прикасаюсь к файлам в исходном каталоге базового уровня. Так что это не «настоящие» ошибки, так сказать, просто раздражающий текст ошибки, от которого я хочу избавиться...

Первая ошибка возникает, когда я запускаю сборку make molecule_test, которая требует от нее каких-то действий. Все, что нужно сделать, делается, но я также получаю:

g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
make: *** [molecule_test] Error 1

..СНОВА сборка завершается успешно, исполняемый файл создается правильно

Вторая ошибка, которую я получаю, возникает, когда ничего не нужно делать... когда это происходит, я получаю:

/bin/sh: -c: line 0: unexpected EOF while looking for matching ``'
/bin/sh: -c: line 1: syntax error: unexpected end of file

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

Мой основной подход заключается в создании базового каталога со следующими

[dir] source/
[dir] data/
[dir] makefiles/
[dir] build/    **gets created
Makefile

Makefile верхнего уровня создает подкаталог в каталоге сборки, копирует необходимые исходники (скажем, для конкретной тестовой программы и необходимые файлы данных, а также makefile для создания всех исходников. Makefile верхнего уровня затем вызывает makefile уровня сборки.

Я был бы открыт для идей о том, как упростить этот процесс, но был бы признателен, если бы мы СНАЧАЛА устранили ошибки.

Заранее спасибо!!!

P.S. Я работаю на Centos 5.4, GNU Make 3.81, gcc версии 4.1.2 20080704 (Red Hat 4.1.2-44).... GNU Make и gcc являются 64-битными версиями...


person Jason R. Mick    schedule 29.06.2010    source источник


Ответы (2)


Это исправление, похоже, устраняет обе ваши ошибки:

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
  molecule_test_prepare_data_files
    @cd $(TEST_DIR)/molecule_unit_test && $(MAKE) ./bin/molecule_test

Команда $(shell ...) предназначена для вызова оболочки вне правила. Нет необходимости использовать его здесь, так как это команда в правиле - это уже происходит в подоболочке. Также обратите внимание, что здесь используется $(MAKE) вместо make (причины немного тонкие, просто считайте это хорошей привычкой).

Можно еще лаконичнее и тише:

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
  molecule_test_prepare_data_files
    @$(MAKE) -s -C $(TEST_DIR)/molecule_unit_test ./bin/molecule_test

Что касается рационализации, вы можете много сделать. Вы можете уменьшить длину вашего второго make-файла примерно наполовину и исправить то, что кажется рядом ошибок, а с первым вы можете сделать еще лучше. Это зависит от того, сколько "магии" вы можете терпеть. Вот быстрая попытка упростить ваш второй Makefile (поскольку у меня нет ваших файлов для его тестирования, я не могу обещать, что он будет работать без некоторых изменений).

CC=g++
CFLAGS=-c -Wall
SOURCE_DIR:=./source
INCDIRS := -I$(SOURCE_DIR)
OBJ_DIR:=./obj
EXE_DIR:=./bin

VPATH = $(SOURCE_DIR)

$(EXE_DIR)/molecule_test : $(OBJ_DIR)/main.o \
  $(OBJ_DIR)/parameter_manager_lj_molecule.o \
  $(OBJ_DIR)/parameter_manager.o $(OBJ_DIR)/parser.o \
  $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
  $(OBJ_DIR)/molecule_reader.o \
  $(OBJ_DIR)/molecule_reader_psf_pdb.o
    @$(CC) $^ -o $@

$(OBJ_DIR)/main.o $(OBJ_DIR)/molecule_reader.o \
  $(OBJ_DIR)/molecule_reader_psf_pdb.o: \
  molecule_manager.h \
  molecule_manager_main.h \
  parameter_manager.h \
  parameter_manager_lj_molecule.h

$(OBJ_DIR)/main.o: main.cpp \
  molecule_reader.h \
  molecule_reader_psf_pdb.h common.h
    $(CC) $(CFLAGS) $(INCDIRS) $< $@

$(OBJ_DIR)/molecule_reader_psf_pdb.o: molecule_reader.h

$(OBJ_DIR)/parameter_manager_lj_molecule.o: parser.h

%.o: %.cpp %.h common.h
    $(CC) $(CFLAGS) $(INCDIRS) $< -o $@
person Beta    schedule 30.06.2010
comment
Я думаю, вам нужно -o перед $@ в последней строке? - person Paul R; 30.06.2010
comment
@Paul R: вы правы, спасибо, исправлено. И, вероятно, у него есть более серьезная ошибка или две, которые выявятся при тестировании. - person Beta; 30.06.2010
comment
Спасибо вам обоим!! Попробую это сейчас. :) - person Jason R. Mick; 08.07.2010
comment
хм, я получаю сообщение об ошибке: make[1]: *** No rule to make target main.cpp', необходимое для obj/main.o'. Stop. ... когда я пытаюсь создать вторую цель... - person Jason R. Mick; 08.07.2010
comment
Хорошо, я исправил свою предыдущую ошибку, изменив .cpp на .cc в цели main.o... но теперь я получаю эту ошибку... obj/main.o: file not recognized: File format not recognized... я читал, что это связано с тем, что у вас неправильное расширение файла... но мое расширение все выглядит прямо сейчас: O - person Jason R. Mick; 08.07.2010
comment
Ура! Мне просто нужно было перекомпилировать. Устаревший объект main.o явно меня смущал... Но да, ваш make-файл был на высоте, так как вы не могли знать, что я назову его *.cc вместо *.cpp. - person Jason R. Mick; 08.07.2010

Для начала вы можете избавиться от всех команд mv и использовать встроенные переменные make, например

$(OBJ_DIR)/parameter_manager.o: $(SOURCE_DIR)/parameter_manager.cpp $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) -o $@ $<
person Paul R    schedule 29.06.2010
comment
Как работают $@ и $<... будут ли они удалять объекты в моей папке объектов (где выполняется моя окончательная сборка)? Я бы хотел держать их отдельно... - person Jason R. Mick; 30.06.2010
comment
@Jason R. Mick: это автоматические переменные (см. Руководство по gnu make). $@ расширяется до цели, а $< расширяется до первой предпосылки. - person Beta; 30.06.2010
comment
Ах. Быстрый вопрос; Я работаю в лаборатории, где большинство людей знают гораздо меньше, чем кодирование, чем я, поэтому поддерживать этот проект может быть, мягко говоря, сложно. Боюсь, что такие милые трюки только усугубят ситуацию. Я не уверен, должен ли я пытаться писать свой код/создавать файлы для удобочитаемости для новичков или, так сказать, для оптимальной грамматики. Предложения?? Также НИКТО не имеет представления о моих первоначальных системных ошибках????? - person Jason R. Mick; 02.07.2010
comment
@Jason: в идеале вы хотите упростить свой make-файл, что может сделать его немного более загадочным, но, надеюсь, это будет перевешиваться простотой обслуживания - посмотрите правила по умолчанию в руководстве GNU make, используя подстановочный знак %. Вы должны иметь возможность установить некоторые правила по умолчанию, а затем вашим менее просвещенным коллегам нужно будет просто обновить зависимости, что должно быть довольно просто и не требует понимания более загадочных частей make-файла. - person Paul R; 02.07.2010
comment
Спасибо. Павел. Тем не менее, до сих пор нет ответов на вышеуказанные ошибки?? КТО-НИБУДЬ? - person Jason R. Mick; 07.07.2010
comment
@Jason: все, что я могу предложить, это то, что вы используете подход «разделяй и властвуй» для отладки вашего make-файла - уменьшите его до минимального make-файла, который просто компилирует один исходный файл и заставляет его работать, а затем поэтапно создаете его до полной версии. - person Paul R; 07.07.2010