我的问题是先决条件(由变量设置)没有执行,并且即使以前手动执行,链接时也找不到所需的库。我的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)”,因为它只是到由pathdefs.mk之前立即创建的ExampleTrunk / BUILD / *目录的另一个路径表示。当我直接调用这两个规则中的任何一个时,它们都可以正确执行。因为我改编了googletest Makefile,所以将根据需要在ExampleTrunk / BUILD / *目录中创建输出。
module.mk文件非常简短,因为它们包含module_commons.mk,其中包含要执行的大多数操作:
ExampleTrunk / src / HelloWorld / module.mk:
additionalPrograms :=
gmockIsRequired := no
include $(PROJECT_ROOT)/buildenv/module_commons.mk
ExampleTrunk / test / 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
它失败,并显示链接器错误(如下所示)。这里是HelloWorld_Test的相关* .mk部分,其中包含已解析的变量,如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.不对make HelloWorld_Test
执行先决条件gtest,并且
2.即使这些库是手动创建的并且因此存在,链接器也找不到它们
感谢您的帮助 茉莉花
答案 0 :(得分:1)
如果它是short, self-contained, correct example,它将帮助人们回答您的问题。这里有太多信息可能与问题无关,因此需要花很多精力才能通读所有内容。取而代之的是,从一个简单的版本开始,该版本是您想要使用简化的makefile做的。如果没有失败,请继续添加部分实际环境,直到出现问题为止。
我怀疑您的问题就在您问题的末尾:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
这不是您想要的,因为这意味着首先构建../BUILD/BIN/HelloWorld_Test
,然后构建gtest
。
实际上,您想要的是在{em> gtest
之前构建../BUILD/BIN/HelloWorld_Test
,否则gtest库不可用。我认为您需要此:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $@
这可确保在尝试链接程序之前已构建gtest
。理想情况下,您希望该目标还列出gtest
库作为先决条件,以便在修改它们后重新构建程序。
答案 1 :(得分:0)
正如之前的评论所述,MadScientist的答案解决了在测试代码本身之前构建gtest的第一个问题,但是链接仍然需要进行一些更改才能起作用。为了在测试源之前构建gtest,我从以下位置更改了构建规则:
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test gtest | $(BUILD_DIRS);
../BUILD/BIN/HelloWorld_Test: $(moduleObj)
g++ $(CPPFLAGS) $(CPPFLAGS_TEST) $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $@
到
HelloWorld_Test: ../BUILD/BIN/HelloWorld_Test;
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
g++ $(CXXFLAGS) $(LDFLAGS_TEST) $^ -o $@
链接过程不需要任何头文件。在链接* .o文件之前,现在已将必需的gtest库构建到../BUILD/LIB,但是在链接过程中仍找不到这些库:
[.....ExampleTrunk/buildenv]$ make HelloWorld_Test -p>log.txt
仍然给出了错误
/usr/bin/ld: cannot find -lgtest
/usr/bin/ld: cannot find -lgtest_main
collect2: error: ld returned 1 exit status
可以通过将规则更改为以下内容来获得链接过程的详细输出来揭示问题:
../BUILD/BIN/HelloWorld_Test: $(moduleObj) | gtest $(BUILD_DIRS)
ld -o $@ $(LDFLAGS_TEST) $^ --verbose
详细输出为:
GNU ld (GNU Binutils) 2.30
...
==================================================
attempt to open ../BUILD/LIB/libgtest.so failed
attempt to open ../BUILD/LIB/libgtest.a failed
...
所以问题是,参数“ -l ...”在库名中添加了前缀“ lib”。因为库名是gtest.a和gtest_main.a,所以找不到它们。解决方案是将变量从以下位置更改:
LDFLAGS_TEST := -L../BUILD/LIB/ -lgtest -lgtest_main
完整路径表示
LDFLAGS_TEST := ../BUILD/LIB/gtest.a ../BUILD/LIB/gtest_main
现在一切正常:D
如果必须链接更多具有“特殊”名称的库,则最好更改相应的Makefile,以在库的输出名称中添加前缀“ lib”。这将使命令行更短,因为路径../BUILD/LIB仅通过“ -L”开关出现一次。尽管如此,我认为对于我来说,完整的路径是可以的,因为参数长度不是很多,而且对googletest的第三方Makefile的操作也较少。
感谢您的帮助, 茉莉花