RunCMakeTest.cmake 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. include(RunCMake)
  2. set(RunCMake_GENERATOR "Ninja")
  3. set(RunCMake_GENERATOR_IS_MULTI_CONFIG 0)
  4. # Detect ninja version so we know what tests can be supported.
  5. execute_process(
  6. COMMAND "${RunCMake_MAKE_PROGRAM}" --version
  7. OUTPUT_VARIABLE ninja_out
  8. ERROR_VARIABLE ninja_out
  9. RESULT_VARIABLE ninja_res
  10. OUTPUT_STRIP_TRAILING_WHITESPACE
  11. )
  12. if(ninja_res EQUAL 0 AND "x${ninja_out}" MATCHES "^x[0-9]+\\.[0-9]+")
  13. set(ninja_version "${ninja_out}")
  14. message(STATUS "ninja version: ${ninja_version}")
  15. else()
  16. message(FATAL_ERROR "'ninja --version' reported:\n${ninja_out}")
  17. endif()
  18. if(CMAKE_HOST_WIN32)
  19. run_cmake(SelectCompilerWindows)
  20. else()
  21. run_cmake(SelectCompilerUNIX)
  22. endif()
  23. function(run_NinjaToolMissing)
  24. set(RunCMake_MAKE_PROGRAM ninja-tool-missing)
  25. run_cmake(NinjaToolMissing)
  26. endfunction()
  27. run_NinjaToolMissing()
  28. function(run_NoWorkToDo)
  29. run_cmake(NoWorkToDo)
  30. set(RunCMake_TEST_NO_CLEAN 1)
  31. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/NoWorkToDo-build)
  32. set(RunCMake_TEST_OUTPUT_MERGE 1)
  33. run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .)
  34. run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain)
  35. endfunction()
  36. run_NoWorkToDo()
  37. function(run_VerboseBuild)
  38. run_cmake(VerboseBuild)
  39. set(RunCMake_TEST_NO_CLEAN 1)
  40. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VerboseBuild-build)
  41. set(RunCMake_TEST_OUTPUT_MERGE 1)
  42. run_cmake_command(VerboseBuild-build ${CMAKE_COMMAND} --build . -v --clean-first)
  43. run_cmake_command(VerboseBuild-nowork ${CMAKE_COMMAND} --build . --verbose)
  44. endfunction()
  45. run_VerboseBuild()
  46. function(run_CMP0058 case)
  47. # Use a single build tree for a few tests without cleaning.
  48. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0058-${case}-build)
  49. set(RunCMake_TEST_NO_CLEAN 1)
  50. file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
  51. file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
  52. run_cmake(CMP0058-${case})
  53. run_cmake_command(CMP0058-${case}-build ${CMAKE_COMMAND} --build .)
  54. endfunction()
  55. run_CMP0058(OLD-no)
  56. run_CMP0058(OLD-by)
  57. run_CMP0058(WARN-no)
  58. run_CMP0058(WARN-by)
  59. run_CMP0058(NEW-no)
  60. run_CMP0058(NEW-by)
  61. run_cmake(CustomCommandDepfile)
  62. run_cmake(CustomCommandJobPool)
  63. run_cmake(JobPoolUsesTerminal)
  64. run_cmake(RspFileC)
  65. run_cmake(RspFileCXX)
  66. if(TEST_Fortran)
  67. run_cmake(RspFileFortran)
  68. endif()
  69. function(run_CommandConcat)
  70. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CommandConcat-build)
  71. set(RunCMake_TEST_NO_CLEAN 1)
  72. file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
  73. file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
  74. run_cmake(CommandConcat)
  75. run_cmake_command(CommandConcat-build ${CMAKE_COMMAND} --build .)
  76. endfunction()
  77. run_CommandConcat()
  78. function(run_SubDir)
  79. # Use a single build tree for a few tests without cleaning.
  80. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubDir-build)
  81. set(RunCMake_TEST_NO_CLEAN 1)
  82. file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
  83. file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
  84. run_cmake(SubDir)
  85. if(WIN32)
  86. set(SubDir_all [[SubDir\all]])
  87. set(SubDir_test [[SubDir\test]])
  88. set(SubDir_install [[SubDir\install]])
  89. set(SubDirBinary_test [[SubDirBinary\test]])
  90. set(SubDirBinary_all [[SubDirBinary\all]])
  91. set(SubDirBinary_install [[SubDirBinary\install]])
  92. else()
  93. set(SubDir_all [[SubDir/all]])
  94. set(SubDir_test [[SubDir/test]])
  95. set(SubDir_install [[SubDir/install]])
  96. set(SubDirBinary_all [[SubDirBinary/all]])
  97. set(SubDirBinary_test [[SubDirBinary/test]])
  98. set(SubDirBinary_install [[SubDirBinary/install]])
  99. endif()
  100. run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
  101. run_cmake_command(SubDir-test ${CMAKE_COMMAND} --build . --target ${SubDir_test})
  102. run_cmake_command(SubDir-install ${CMAKE_COMMAND} --build . --target ${SubDir_install})
  103. run_cmake_command(SubDirBinary-build ${CMAKE_COMMAND} --build . --target ${SubDirBinary_all})
  104. run_cmake_command(SubDirBinary-test ${CMAKE_COMMAND} --build . --target ${SubDirBinary_test})
  105. run_cmake_command(SubDirBinary-install ${CMAKE_COMMAND} --build . --target ${SubDirBinary_install})
  106. endfunction()
  107. run_SubDir()
  108. function(run_ninja dir)
  109. execute_process(
  110. COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
  111. WORKING_DIRECTORY "${dir}"
  112. OUTPUT_VARIABLE ninja_stdout
  113. ERROR_VARIABLE ninja_stderr
  114. RESULT_VARIABLE ninja_result
  115. )
  116. if(NOT ninja_result EQUAL 0)
  117. message(STATUS "
  118. ============ beginning of ninja's stdout ============
  119. ${ninja_stdout}
  120. =============== end of ninja's stdout ===============
  121. ")
  122. message(STATUS "
  123. ============ beginning of ninja's stderr ============
  124. ${ninja_stderr}
  125. =============== end of ninja's stderr ===============
  126. ")
  127. message(FATAL_ERROR
  128. "top ninja build failed exited with status ${ninja_result}")
  129. endif()
  130. set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
  131. endfunction(run_ninja)
  132. function (run_LooseObjectDepends)
  133. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LooseObjectDepends-build)
  134. run_cmake(LooseObjectDepends)
  135. run_ninja("${RunCMake_TEST_BINARY_DIR}" "CMakeFiles/top.dir/top.c${CMAKE_C_OUTPUT_EXTENSION}")
  136. if (EXISTS "${RunCMake_TEST_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}dep${CMAKE_SHARED_LIBRARY_SUFFIX}")
  137. message(FATAL_ERROR
  138. "The `dep` library was created when requesting an object file to be "
  139. "built; this should no longer be necessary.")
  140. endif ()
  141. if (EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/dep.dir/dep.c${CMAKE_C_OUTPUT_EXTENSION}")
  142. message(FATAL_ERROR
  143. "The `dep.c` object file was created when requesting an object file to "
  144. "be built; this should no longer be necessary.")
  145. endif ()
  146. endfunction ()
  147. run_LooseObjectDepends()
  148. function (run_AssumedSources)
  149. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build)
  150. run_cmake(AssumedSources)
  151. run_ninja("${RunCMake_TEST_BINARY_DIR}" "target.c")
  152. if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/target.c")
  153. message(FATAL_ERROR
  154. "Dependencies for an assumed source did not hook up properly for 'target.c'.")
  155. endif ()
  156. run_ninja("${RunCMake_TEST_BINARY_DIR}" "target-no-depends.c")
  157. if (EXISTS "${RunCMake_TEST_BINARY_DIR}/target-no-depends.c")
  158. message(FATAL_ERROR
  159. "Dependencies for an assumed source were magically hooked up for 'target-no-depends.c'.")
  160. endif ()
  161. endfunction ()
  162. run_AssumedSources()
  163. function(sleep delay)
  164. execute_process(
  165. COMMAND ${CMAKE_COMMAND} -E sleep ${delay}
  166. RESULT_VARIABLE result
  167. )
  168. if(NOT result EQUAL 0)
  169. message(FATAL_ERROR "failed to sleep for ${delay} second.")
  170. endif()
  171. endfunction(sleep)
  172. macro(ninja_escape_path path out)
  173. string(REPLACE "\$ " "\$\$" "${out}" "${path}")
  174. string(REPLACE " " "\$ " "${out}" "${${out}}")
  175. string(REPLACE ":" "\$:" "${out}" "${${out}}")
  176. endmacro(ninja_escape_path)
  177. macro(shell_escape string out)
  178. string(REPLACE "\"" "\\\"" "${out}" "${string}")
  179. endmacro(shell_escape)
  180. function(run_sub_cmake test ninja_output_path_prefix)
  181. set(top_build_dir "${RunCMake_BINARY_DIR}/${test}-build/")
  182. file(REMOVE_RECURSE "${top_build_dir}")
  183. file(MAKE_DIRECTORY "${top_build_dir}")
  184. ninja_escape_path("${ninja_output_path_prefix}"
  185. escaped_ninja_output_path_prefix)
  186. # Generate top build ninja file.
  187. set(top_build_ninja "${top_build_dir}/build.ninja")
  188. shell_escape("${top_build_ninja}" escaped_top_build_ninja)
  189. set(build_ninja_dep "${top_build_dir}/build_ninja_dep")
  190. ninja_escape_path("${build_ninja_dep}" escaped_build_ninja_dep)
  191. shell_escape("${CMAKE_COMMAND}" escaped_CMAKE_COMMAND)
  192. file(WRITE "${build_ninja_dep}" "fake dependency of top build.ninja file\n")
  193. if(WIN32)
  194. set(cmd_prefix "cmd.exe /C \"")
  195. set(cmd_suffix "\"")
  196. else()
  197. set(cmd_prefix "")
  198. set(cmd_suffix "")
  199. endif()
  200. set(fs_delay 3) # We assume the system as 1 sec timestamp resolution.
  201. file(WRITE "${top_build_ninja}" "\
  202. subninja ${escaped_ninja_output_path_prefix}/build.ninja
  203. default ${escaped_ninja_output_path_prefix}/all
  204. # Sleep for long enough before regenerating to make sure the timestamp of
  205. # the top build.ninja will be strictly greater than the timestamp of the
  206. # sub/build.ninja file.
  207. rule RERUN
  208. command = ${cmd_prefix}\"${escaped_CMAKE_COMMAND}\" -E sleep ${fs_delay} && \"${escaped_CMAKE_COMMAND}\" -E touch \"${escaped_top_build_ninja}\"${cmd_suffix}
  209. description = Testing regeneration
  210. generator = 1
  211. build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_path_prefix}/build.ninja
  212. pool = console
  213. ")
  214. # Run sub cmake project.
  215. set(RunCMake_TEST_OPTIONS "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=${ninja_output_path_prefix}")
  216. set(RunCMake_TEST_BINARY_DIR "${top_build_dir}/${ninja_output_path_prefix}")
  217. run_cmake(${test})
  218. # Check there is no 'default' statement in Ninja file generated by CMake.
  219. set(sub_build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja")
  220. file(READ "${sub_build_ninja}" sub_build_ninja_file)
  221. if(sub_build_ninja_file MATCHES "\ndefault [^\n][^\n]*all\n")
  222. message(FATAL_ERROR
  223. "unexpected 'default' statement found in '${sub_build_ninja}'")
  224. endif()
  225. # Run ninja from the top build directory.
  226. run_ninja("${top_build_dir}")
  227. # Test regeneration rules run in order.
  228. set(main_cmakelists "${RunCMake_SOURCE_DIR}/CMakeLists.txt")
  229. sleep(${fs_delay})
  230. file(TOUCH "${main_cmakelists}")
  231. file(TOUCH "${build_ninja_dep}")
  232. run_ninja("${top_build_dir}")
  233. file(TIMESTAMP "${main_cmakelists}" mtime_main_cmakelists UTC)
  234. file(TIMESTAMP "${sub_build_ninja}" mtime_sub_build_ninja UTC)
  235. file(TIMESTAMP "${top_build_ninja}" mtime_top_build_ninja UTC)
  236. # Check sub build.ninja is regenerated.
  237. if(mtime_main_cmakelists STRGREATER mtime_sub_build_ninja)
  238. message(FATAL_ERROR
  239. "sub build.ninja not regenerated:
  240. CMakeLists.txt = ${mtime_main_cmakelists}
  241. sub/build.ninja = ${mtime_sub_build_ninja}")
  242. endif()
  243. # Check top build.ninja is regenerated after sub build.ninja.
  244. if(NOT mtime_top_build_ninja STRGREATER mtime_sub_build_ninja)
  245. message(FATAL_ERROR
  246. "top build.ninja not regenerated strictly after sub build.ninja:
  247. sub/build.ninja = ${mtime_sub_build_ninja}
  248. build.ninja = ${mtime_top_build_ninja}")
  249. endif()
  250. endfunction(run_sub_cmake)
  251. if("${ninja_version}" VERSION_LESS 1.6)
  252. message(WARNING "Ninja is too old; skipping rest of test.")
  253. return()
  254. endif()
  255. foreach(ninja_output_path_prefix "sub space" "sub")
  256. run_sub_cmake(Executable "${ninja_output_path_prefix}")
  257. run_sub_cmake(StaticLib "${ninja_output_path_prefix}")
  258. run_sub_cmake(SharedLib "${ninja_output_path_prefix}")
  259. run_sub_cmake(TwoLibs "${ninja_output_path_prefix}")
  260. run_sub_cmake(SubDirPrefix "${ninja_output_path_prefix}")
  261. run_sub_cmake(CustomCommandWorkingDirectory "${ninja_output_path_prefix}")
  262. endforeach(ninja_output_path_prefix)
  263. function (run_PreventTargetAliasesDupBuildRule)
  264. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PreventTargetAliasesDupBuildRule-build)
  265. run_cmake(PreventTargetAliasesDupBuildRule)
  266. run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err)
  267. endfunction ()
  268. run_PreventTargetAliasesDupBuildRule()
  269. function (run_PreventConfigureFileDupBuildRule)
  270. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/PreventConfigureFileDupBuildRule-build)
  271. run_cmake(PreventConfigureFileDupBuildRule)
  272. run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err)
  273. endfunction()
  274. run_PreventConfigureFileDupBuildRule()
  275. function (run_ChangeBuildType)
  276. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ChangeBuildType-build)
  277. set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=Debug")
  278. run_cmake(ChangeBuildType)
  279. unset(RunCMake_TEST_OPTIONS)
  280. run_ninja("${RunCMake_TEST_BINARY_DIR}" -w dupbuild=err)
  281. endfunction()
  282. run_ChangeBuildType()
  283. function(run_Qt5AutoMocDeps)
  284. if(CMake_TEST_Qt5 AND CMAKE_TEST_Qt5Core_Version VERSION_GREATER_EQUAL 5.15.0)
  285. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Qt5AutoMocDeps-build)
  286. set(RunCMake_TEST_OPTIONS "-DQt5Core_DIR=${Qt5Core_DIR}" "-DQt5Widgets_DIR=${Qt5Widgets_DIR}")
  287. run_cmake(Qt5AutoMocDeps)
  288. unset(RunCMake_TEST_OPTIONS)
  289. # Build the project.
  290. run_ninja("${RunCMake_TEST_BINARY_DIR}")
  291. # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
  292. # for app_with_qt target.
  293. file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
  294. # Build and assert that AUTOMOC was not run for app_with_qt.
  295. run_ninja("${RunCMake_TEST_BINARY_DIR}")
  296. if(ninja_stdout MATCHES "Automatic MOC for target app_with_qt")
  297. message(FATAL_ERROR
  298. "AUTOMOC should not have executed for 'app_with_qt' target:\nstdout:\n${ninja_stdout}")
  299. endif()
  300. # Assert that the subdir executables were not rebuilt.
  301. if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_1")
  302. message(FATAL_ERROR
  303. "AUTOMOC should not have executed for 'sub_exe_1' target:\nstdout:\n${ninja_stdout}")
  304. endif()
  305. if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_2")
  306. message(FATAL_ERROR
  307. "AUTOMOC should not have executed for 'sub_exe_2' target:\nstdout:\n${ninja_stdout}")
  308. endif()
  309. # Touch a header file to make sure an automoc dependency cycle is not introduced.
  310. file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
  311. run_ninja("${RunCMake_TEST_BINARY_DIR}")
  312. # Need to run a second time to hit the dependency cycle.
  313. run_ninja("${RunCMake_TEST_BINARY_DIR}")
  314. endif()
  315. endfunction()
  316. run_Qt5AutoMocDeps()