如何在cmake生成的Makefile中编译所有目标的子集,这些目标在bash for循环中以给定的前缀开始



我想编写一个shell脚本,它将只编译和执行我的项目中的单元测试二进制文件。与单元测试相关的Makefile(由CMake生成)中的所有目标都以UnitTest开头,例如UnitTestArray。我如何在shell脚本中编写for循环来单独编译所有单元测试二进制文件?我知道如何在循环中依次运行预编译的二进制文件。这只是我不确定的编译部分。请看下面的shell脚本。

WORKDIR=$PWD
BUILD=~/Dropbox/Projects/Apeiron/build
BIN=$BUILD/bin
# Compile test binaries
cd $BUILD
for target in [UnitTest* in Makefile] # I'm not sure how to write this for loop.
do
echo ""
echo "Compiling $target" 
make -j 8 $target 
done
# Run tests
cd $BIN
for test in UnitTest*
do
echo ""
echo "Running $test" 
./$test 
done
cd $WORKDIR
以下是生成的Makefile中的一些示例目标规则:
#=============================================================================
# Target rules for targets named UnitTestApeiron
# Build rule for target.
UnitTestApeiron: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 UnitTestApeiron
.PHONY : UnitTestApeiron
# fast build rule for target.
UnitTestApeiron/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/UnitTestApeiron.dir/build.make CMakeFiles/UnitTestApeiron.dir/build
.PHONY : UnitTestApeiron/fast
#=============================================================================
# Target rules for targets named UnitTestArray
# Build rule for target.
UnitTestArray: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 UnitTestArray
.PHONY : UnitTestArray
# fast build rule for target.
UnitTestArray/fast:
$(MAKE) $(MAKESILENT) -f CMakeFiles/UnitTestArray.dir/build.make CMakeFiles/UnitTestArray.dir/build
.PHONY : UnitTestArray/fast

我采用了@MadScientist和@Saboteur用户在上述评论中建议的解决方案。以下是使用@Saboteur的解决方案修改我的shell脚本:

WORKDIR=$PWD
BUILD=~/Dropbox/Projects/Apeiron/build
BIN=$BUILD/bin
# Compile test binaries
cd $BUILD
declare -a TargetList=$(grep "cmake_check_build_system" $BUILD/Makefile|cut -d":" -f1)
for target in ${TargetList[@]}; do
# Check if target keyword contains the substring "UnitTest"
if grep -q "UnitTest" <<< "$target"; then
printf "nCompiling $targetn" 
make -j 16 $target
fi
done
# Run tests
cd $BIN
for test in UnitTest*
do
printf "nRunning $testn" 
./$test 
done
cd $WORKDIR

这会产生以下输出:

$ ./unit_test.sh 
Compiling UnitTestArray
[ 16%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 33%] Linking CXX static library ../../../lib/libgtest.a
[ 33%] Built target gtest
[ 50%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[ 66%] Linking CXX static library ../../../lib/libgtest_main.a
[ 66%] Built target gtest_main
[ 83%] Building CXX object CMakeFiles/UnitTestArray.dir/libs/DataContainers/test/UnitTestArray.cpp.o
[100%] Linking CXX executable bin/UnitTestArray
[100%] Built target UnitTestArray
Compiling UnitTestApeiron
Consolidate compiler generated dependencies of target gtest
[ 33%] Built target gtest
Consolidate compiler generated dependencies of target gtest_main
[ 66%] Built target gtest_main
[ 83%] Building CXX object CMakeFiles/UnitTestApeiron.dir/test/UnitTestApeiron.cpp.o
[100%] Linking CXX executable bin/UnitTestApeiron
[100%] Built target UnitTestApeiron
Running UnitTestApeiron
Running main() from /home/niran90/Dropbox/Projects/Apeiron/build/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 15 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 15 tests from ApeironTest
[ RUN      ] ApeironTest.Min
[       OK ] ApeironTest.Min (0 ms)
[ RUN      ] ApeironTest.Max
[       OK ] ApeironTest.Max (0 ms)
[ RUN      ] ApeironTest.MinMax
[       OK ] ApeironTest.MinMax (0 ms)
[ RUN      ] ApeironTest.Bound
[       OK ] ApeironTest.Bound (0 ms)
[ RUN      ] ApeironTest.Sgn
[       OK ] ApeironTest.Sgn (0 ms)
[ RUN      ] ApeironTest.Abs
[       OK ] ApeironTest.Abs (0 ms)
[ RUN      ] ApeironTest.Floor
[       OK ] ApeironTest.Floor (0 ms)
[ RUN      ] ApeironTest.Ceil
[       OK ] ApeironTest.Ceil (0 ms)
[ RUN      ] ApeironTest.Round
[       OK ] ApeironTest.Round (0 ms)
[ RUN      ] ApeironTest.isEqual
[       OK ] ApeironTest.isEqual (0 ms)
[ RUN      ] ApeironTest.isLess
[       OK ] ApeironTest.isLess (0 ms)
[ RUN      ] ApeironTest.isLessEqual
[       OK ] ApeironTest.isLessEqual (0 ms)
[ RUN      ] ApeironTest.isLarger
[       OK ] ApeironTest.isLarger (0 ms)
[ RUN      ] ApeironTest.isLargerEqual
[       OK ] ApeironTest.isLargerEqual (1 ms)
[ RUN      ] ApeironTest.isBounded
[       OK ] ApeironTest.isBounded (0 ms)
[----------] 15 tests from ApeironTest (1 ms total)
[----------] Global test environment tear-down
[==========] 15 tests from 1 test suite ran. (1 ms total)
[  PASSED  ] 15 tests.
Running UnitTestArray
Running main() from /home/niran90/Dropbox/Projects/Apeiron/build/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from ArrayTest
[ RUN      ] ArrayTest.Initialisation
[       OK ] ArrayTest.Initialisation (0 ms)
[ RUN      ] ArrayTest.AssignmentOperator
[       OK ] ArrayTest.AssignmentOperator (0 ms)
[----------] 2 tests from ArrayTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

我可以看到@MadScientist的建议使用CTest是非常有用的,当gtest单元测试的数量相当大。这是因为CTest提供了通过/失败测试的简明摘要。我必须配置我的项目CMakeLists.txt如下:

enable_testing()
# Fetch GoogleTest directories
include(FetchContent)
FetchContent_Declare(GoogleTest URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip)
FetchContent_MakeAvailable(GoogleTest)
# Add unit test executables
add_executable(UnitTestApeiron ${PROJECT_SOURCE_DIR}/test/UnitTestApeiron.cpp)
add_executable(UnitTestArray ${PROJECT_SOURCE_DIR}/libs/DataContainers/test/UnitTestArray.cpp)
# Link with gtest, gtest_main, and associated libraries.
target_link_libraries(UnitTestApeiron gtest gtest_main)
target_link_libraries(UnitTestArray gtest gtest_main)
add_test(UnitTestApeiron ${BIN_DIRECTORY}/UnitTestApeiron)
add_test(UnitTestArray ${BIN_DIRECTORY}/UnitTestArray)

然后,简单地在项目构建目录中运行ctest得到:

$ ctest 
Test project /home/niran90/Dropbox/Projects/Apeiron/build
Start 1: UnitTestApeiron
1/2 Test #1: UnitTestApeiron ..................   Passed    0.01 sec
Start 2: UnitTestArray
2/2 Test #2: UnitTestArray ....................   Passed    0.00 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) =   0.01 sec