如何在 bash for 循环中编译以给定前缀开头的 CMake 生成的 Makefile 中所有目标的子集

How to compile a subset of all targets in a CMake-generated Makefile that start with a given prefix in a bash for loop

我想编写一个 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 用户建议的解决方案。以下是我的shell脚本使用@Saboteur的方案修改:

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 $target\n" 
     make -j 16 $target
  fi
done

# Run tests
cd $BIN
for test in UnitTest*
do
  printf "\nRunning $test\n" 
  ./$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.

gtest 单元测试的数量非常大时,我可以看到@MadScientist 使用 CTest 的建议非常有用。这是因为 CTest 提供了 passed/failed 测试的简明摘要。我必须按如下方式配置我的项目 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