如何在 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
我想编写一个 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