Моя проблема заключается в том, что предварительное условие (заданное переменной) не выполняется, и даже если раньше оно выполнялось вручную, необходимые библиотеки не будут найдены при компоновке. Мои Makefile не являются рекурсивными, но обязательным условием является внешнее программное обеспечение googletest, и, следовательно, он вызывается рекурсивно, так как источники не меняются между несколькими запусками «make».
Моя среда:
$ make --version
GNU Make 4.2.1
Built for x86_64-unknown-linux-gnu
Copyright (C) 1988-2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ uname -a
Linux archlinux 4.17.3-1-ARCH #1 SMP PREEMPT Tue Jun 26 04:42:36 UTC 2018 x86_64 GNU/Linux
Это соответствующая структура каталогов:
|-- ExampleTrunk
| |-- BUILD
| | |-- BIN
| | |-- LIB
| | `-- OBJ
| |-- buildenv
| | |-- ...
| | |-- GTEST.mk
| | |-- Makefile
| | |-- commons.mk
| | |-- module_commons.mk
| | |-- pathdefs.mk
| |-- src
| `-- HelloWorld
| `-- module.mk
| `-- test
| `-- HelloWorld
| `-- module.mk
`-- Tools
`-- GoogleTest
`-- googletest
|-- make
|-- Makefile
Папка ExampleTrunk / BUILD и ее подкаталоги немедленно создаются в pathdefs.mk и поэтому существуют до дальнейшего выполнения правила.
Начальный Makefile: ExampleTrunk / buildenv / Makefile:
...
export SHELL := $(shell which bash)
...
.DEFAULT_GOAL := all
#some common function definitions
include commons.mk
#get $(PROJECT_ROOT), $(REL_PROJECT_ROOT); immediately create $(GLOBAL_OBJ_PATH), $(GLOBAL_LIB_PATH), $(GLOBAL_BIN_PATH)
include pathdefs.mk
export BUILD_DIRS := $(GLOBAL_BIN_PATH) $(GLOBAL_LIB_PATH) $(GLOBAL_OBJ_PATH)
include $(REL_PROJECT_ROOT)/src/HelloWorld/module.mk
...
#only include test stuff, when tests shall be built or cleaned to save dependency calculation time in all other cases
ifeq "$(call givenStringEndsWithTest,$(MAKECMDGOALS))" "0"
include GTEST.mk
include $(REL_PROJECT_ROOT)/test/HelloWorld/module.mk
endif
all : $(programs) | $(BUILD_DIRS);
all_Test: $(testPrograms) | $(BUILD_DIRS);
$(GLOBAL_OBJ_PATH)/%.o: %.cpp | $(BUILD_DIRS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
clean:
$(RM) $(addprefix $(GLOBAL_BIN_PATH)/,$(programs)) $(addprefix $(GLOBAL_BIN_PATH)/,$(testPrograms))
$(RM) $(objectsToClean)
$(RM) $(objectsToClean:.o=.d)
clean_Test: clean gtest_clean gmock_clean;
Правила "all", "clean" и "clean_Test" разрешают и выполняют свои предварительные условия, как ожидается, и работают без ошибок. (оценивается make ... -p
) Правило "all_Test" выдает ошибки, как описано ниже.
GTest.mk содержит следующие рекурсивные правила:
gtest: | $(BUILD_DIRS)
[ -d "$(REL_GTEST_MAKEFILE_DIR)" ] && cd "$(REL_GTEST_MAKEFILE_DIR)" && $(MAKE)
gtest_clean:
[ -d "$(REL_GTEST_MAKEFILE_DIR)" ] && cd "$(REL_GTEST_MAKEFILE_DIR)" && $(MAKE) clean
Каталог "$ (REL_GTEST_MAKEFILE_DIR)" существует, потому что это всего лишь другое представление пути к каталогам ExampleTrunk / BUILD / *, которые были непосредственно созданы ранее pathdefs.mk. Когда я вызываю любое из обоих правил напрямую, они могут быть выполнены без ошибок. Поскольку я адаптировал Makefile googletest, выходные данные создаются в каталогах ExampleTrunk / BUILD / * по мере необходимости.
Файлы module.mk очень короткие, так как включают в себя module_commons.mk, который содержит большинство выполняемых вещей:
ExampleTrunk / SRC / HelloWorld / module.mk:
additionalPrograms :=
gmockIsRequired := no
include $(PROJECT_ROOT)/buildenv/module_commons.mk
ExampleTrunk / тест / HelloWorld / module.mk:
additionalPrograms := HelloWorld
gmockIsRequired := no
include $(PROJECT_ROOT)/buildenv/module_commons.mk
ExampleTrunk / buildenv / module_commons.mk генерирует большинство переменных автоматически из расположения и настроек файла module.mk.
Когда я выхожу
[.....ExampleTrunk/buildenv]$ make HelloWorld
все работает нормально, но с
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test
происходит сбой с ошибкой компоновщика (показано ниже). Вот соответствующие части * .mk для HelloWorld_Test с уже разрешенными переменными, как показано в make HelloWorld_Test -p
(переменные с файлами / каталогами или флагами не записаны в разрешенной форме для краткости):
...
LDFLAGS_TEST := -L../BUILD/LIB -lgtest -lgtest_main
CPPFLAGS_TEST := $(googleStuffCPPFLAGS) # these are -I arguments for GCC, generated in GTEST.mk
moduleObj := "this variable is generated correctly by functions..."
...
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
../BUILD/BIN/HelloWorld_Test: $(moduleObj)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $@
Когда я выхожу
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test -p>log.txt
log.txt показывает, что переменные правила разрешены правильно, но я получаю ошибку
/usr/bin/ld: cannot find -lgtest
/usr/bin/ld: cannot find -lgtest_main
collect2: error: ld returned 1 exit status
Это потому, что правило "gtest" не запускалось раньше, но даже если я запускаю его вручную
[.....ExampleTrunk/buildenv]$ make gtest
и все необходимые файлы созданы в правильном каталоге ../BUILD/*, ошибка все еще остается.
В чем может быть причина, почему
1. Предварительное условие gtest не выполняется для make HelloWorld_Test
и
2. даже если библиотеки созданы вручную и поэтому существуют, они не найдены компоновщиком
Спасибо за вашу помощь
Jasmin