CTestUpdateCommon.cmake 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #-----------------------------------------------------------------------------
  2. # Function to run a child process and report output only on error.
  3. function(run_child)
  4. execute_process(${ARGN}
  5. RESULT_VARIABLE FAILED
  6. OUTPUT_VARIABLE OUTPUT
  7. ERROR_VARIABLE OUTPUT
  8. OUTPUT_STRIP_TRAILING_WHITESPACE
  9. ERROR_STRIP_TRAILING_WHITESPACE
  10. )
  11. if(FAILED)
  12. string(REPLACE "\n" "\n " OUTPUT "${OUTPUT}")
  13. message(FATAL_ERROR "Child failed (${FAILED}), output is\n ${OUTPUT}\n"
  14. "Command = [${ARGN}]\n")
  15. endif()
  16. # Pass output back up to the parent scope for possible further inspection.
  17. set(OUTPUT "${OUTPUT}" PARENT_SCOPE)
  18. endfunction()
  19. #-----------------------------------------------------------------------------
  20. # Function to find the Update.xml file and check for expected entries.
  21. function(check_updates build)
  22. # Find the Update.xml file for the given build tree
  23. set(PATTERN ${TOP}/${build}/Testing/*/Update.xml)
  24. file(GLOB UPDATE_XML_FILE RELATIVE ${TOP} ${PATTERN})
  25. string(REGEX REPLACE "//Update.xml$" "/Update.xml"
  26. UPDATE_XML_FILE "${UPDATE_XML_FILE}"
  27. )
  28. if(NOT UPDATE_XML_FILE)
  29. message(FATAL_ERROR "Cannot find Update.xml with pattern\n ${PATTERN}")
  30. endif()
  31. message(" found ${UPDATE_XML_FILE}")
  32. set(max_update_xml_size 16384)
  33. # Read entries from the Update.xml file
  34. set(types "Updated|Modified|Conflicting")
  35. file(STRINGS ${TOP}/${UPDATE_XML_FILE} UPDATE_XML_ENTRIES
  36. REGEX "<(${types}|FullName)>"
  37. LIMIT_INPUT ${max_update_xml_size}
  38. )
  39. string(REGEX REPLACE
  40. "[ \t]*<(${types})>[ \t]*;[ \t]*<FullName>([^<]*)</FullName>"
  41. "\\1{\\2}" UPDATE_XML_ENTRIES "${UPDATE_XML_ENTRIES}")
  42. # If specified, remove the given prefix from the files in Update.xml.
  43. # Some VCS systems, like Perforce, return absolute locations
  44. if(DEFINED REPOSITORY_FILE_PREFIX)
  45. string(REPLACE
  46. "${REPOSITORY_FILE_PREFIX}" ""
  47. UPDATE_XML_ENTRIES "${UPDATE_XML_ENTRIES}")
  48. endif()
  49. # Compare expected and actual entries
  50. set(EXTRA "${UPDATE_XML_ENTRIES}")
  51. list(REMOVE_ITEM EXTRA ${ARGN} ${UPDATE_EXTRA} ${UPDATE_MAYBE})
  52. set(MISSING "${ARGN}" ${UPDATE_EXTRA})
  53. if(NOT "" STREQUAL "${UPDATE_XML_ENTRIES}")
  54. list(REMOVE_ITEM MISSING ${UPDATE_XML_ENTRIES})
  55. endif()
  56. if(NOT UPDATE_NOT_GLOBAL)
  57. set(rev_elements Revision PriorRevision ${UPDATE_GLOBAL_ELEMENTS})
  58. string(REPLACE ";" "|" rev_regex "${rev_elements}")
  59. set(rev_regex "^\t<(${rev_regex})>[^<\n]+</(${rev_regex})>$")
  60. file(STRINGS ${TOP}/${UPDATE_XML_FILE} UPDATE_XML_REVISIONS
  61. REGEX "${rev_regex}"
  62. LIMIT_INPUT ${max_update_xml_size}
  63. )
  64. foreach(r IN LISTS UPDATE_XML_REVISIONS)
  65. string(REGEX REPLACE "${rev_regex}" "\\1" element "${r}")
  66. set(element_${element} 1)
  67. endforeach()
  68. foreach(element ${rev_elements})
  69. if(NOT element_${element})
  70. list(APPEND MISSING "global <${element}> element")
  71. endif()
  72. endforeach()
  73. endif()
  74. # Report the result
  75. set(MSG "")
  76. if(MISSING)
  77. # List the missing entries
  78. string(APPEND MSG "Update.xml is missing expected entries:\n")
  79. foreach(f ${MISSING})
  80. string(APPEND MSG " ${f}\n")
  81. endforeach()
  82. else()
  83. # Success
  84. message(" no entries missing from Update.xml")
  85. endif()
  86. # Report the result
  87. if(EXTRA)
  88. # List the extra entries
  89. string(APPEND MSG "Update.xml has extra unexpected entries:\n")
  90. foreach(f ${EXTRA})
  91. string(APPEND MSG " ${f}\n")
  92. endforeach()
  93. else()
  94. # Success
  95. message(" no extra entries in Update.xml")
  96. endif()
  97. if(MSG)
  98. # Provide the log file
  99. file(GLOB UPDATE_LOG_FILE
  100. ${TOP}/${build}/Testing/Temporary/LastUpdate*.log)
  101. if(UPDATE_LOG_FILE)
  102. file(READ ${UPDATE_LOG_FILE} UPDATE_LOG LIMIT ${max_update_xml_size})
  103. string(REPLACE "\n" "\n " UPDATE_LOG "${UPDATE_LOG}")
  104. string(APPEND MSG "Update log:\n ${UPDATE_LOG}")
  105. else()
  106. string(APPEND MSG "No update log found!")
  107. endif()
  108. # Display the error message
  109. message(FATAL_ERROR "${MSG}")
  110. endif()
  111. endfunction()
  112. #-----------------------------------------------------------------------------
  113. # Function to create initial content.
  114. function(create_content dir)
  115. file(MAKE_DIRECTORY ${TOP}/${dir})
  116. # An example CTest project configuration file.
  117. file(WRITE ${TOP}/${dir}/CTestConfig.cmake
  118. "# CTest Configuration File
  119. set(CTEST_PROJECT_NAME TestProject)
  120. set(CTEST_NIGHTLY_START_TIME \"21:00:00 EDT\")
  121. ")
  122. # Some other files.
  123. file(WRITE ${TOP}/${dir}/foo.txt "foo\n")
  124. file(WRITE ${TOP}/${dir}/bar.txt "bar\n")
  125. endfunction()
  126. #-----------------------------------------------------------------------------
  127. # Function to update content.
  128. function(update_content dir added_var removed_var dirs_var)
  129. file(APPEND ${TOP}/${dir}/foo.txt "foo line 2\n")
  130. file(WRITE ${TOP}/${dir}/zot.txt "zot\n")
  131. file(REMOVE ${TOP}/${dir}/bar.txt)
  132. file(MAKE_DIRECTORY ${TOP}/${dir}/subdir)
  133. file(WRITE ${TOP}/${dir}/subdir/foo.txt "foo\n")
  134. file(WRITE ${TOP}/${dir}/subdir/bar.txt "bar\n")
  135. set(${dirs_var} subdir PARENT_SCOPE)
  136. set(${added_var} zot.txt subdir/foo.txt subdir/bar.txt PARENT_SCOPE)
  137. set(${removed_var} bar.txt PARENT_SCOPE)
  138. endfunction()
  139. #-----------------------------------------------------------------------------
  140. # Function to change existing files
  141. function(change_content dir)
  142. file(APPEND ${TOP}/${dir}/foo.txt "foo line 3\n")
  143. file(APPEND ${TOP}/${dir}/subdir/foo.txt "foo line 2\n")
  144. endfunction()
  145. #-----------------------------------------------------------------------------
  146. # Function to create local modifications before update
  147. function(modify_content dir)
  148. file(APPEND ${TOP}/${dir}/CTestConfig.cmake "# local modification\n")
  149. endfunction()
  150. #-----------------------------------------------------------------------------
  151. # Function to write CTestConfiguration.ini content.
  152. function(create_build_tree src_dir bin_dir)
  153. file(MAKE_DIRECTORY ${TOP}/${bin_dir})
  154. file(WRITE ${TOP}/${bin_dir}/CTestConfiguration.ini
  155. "# CTest Configuration File
  156. SourceDirectory: ${TOP}/${src_dir}
  157. BuildDirectory: ${TOP}/${bin_dir}
  158. Site: test.site
  159. BuildName: user-test
  160. ")
  161. endfunction()
  162. #-----------------------------------------------------------------------------
  163. # Function to write the dashboard test script.
  164. function(create_dashboard_script bin_dir custom_text)
  165. if (NOT ctest_update_check)
  166. set(ctest_update_check [[
  167. if(ret LESS 0)
  168. message(FATAL_ERROR "ctest_update failed with ${ret}")
  169. endif()
  170. ]])
  171. endif()
  172. # Write the dashboard script.
  173. file(WRITE ${TOP}/${bin_dir}.cmake
  174. "# CTest Dashboard Script
  175. set(CTEST_DASHBOARD_ROOT \"${TOP}\")
  176. set(CTEST_SITE test.site)
  177. set(CTEST_BUILD_NAME dash-test)
  178. set(CTEST_SOURCE_DIRECTORY \${CTEST_DASHBOARD_ROOT}/dash-source)
  179. set(CTEST_BINARY_DIRECTORY \${CTEST_DASHBOARD_ROOT}/${bin_dir})
  180. ${custom_text}
  181. # Start a dashboard and run the update step
  182. ctest_start(Experimental)
  183. ctest_update(SOURCE \${CTEST_SOURCE_DIRECTORY} RETURN_VALUE ret ${ctest_update_args})
  184. ${ctest_update_check}")
  185. endfunction()
  186. #-----------------------------------------------------------------------------
  187. # Function to run the dashboard through the command line
  188. function(run_dashboard_command_line bin_dir)
  189. run_child(
  190. WORKING_DIRECTORY ${TOP}/${bin_dir}
  191. COMMAND ${CMAKE_CTEST_COMMAND} -M Experimental -T Start -T Update
  192. )
  193. # Verify the updates reported by CTest.
  194. list(APPEND UPDATE_MAYBE Updated{subdir})
  195. set(_modified Modified{CTestConfig.cmake})
  196. if(UPDATE_NO_MODIFIED)
  197. set(_modified "")
  198. endif()
  199. check_updates(${bin_dir}
  200. Updated{foo.txt}
  201. Updated{bar.txt}
  202. Updated{zot.txt}
  203. Updated{subdir/foo.txt}
  204. Updated{subdir/bar.txt}
  205. ${_modified}
  206. )
  207. endfunction()
  208. #-----------------------------------------------------------------------------
  209. # Function to find the Update.xml file and make sure
  210. # it only has the Revision in it and no updates
  211. function(check_no_update bin_dir)
  212. set(PATTERN ${TOP}/${bin_dir}/Testing/*/Update.xml)
  213. file(GLOB UPDATE_XML_FILE RELATIVE ${TOP} ${PATTERN})
  214. string(REGEX REPLACE "//Update.xml$" "/Update.xml"
  215. UPDATE_XML_FILE "${UPDATE_XML_FILE}")
  216. message(" found ${UPDATE_XML_FILE}")
  217. set(rev_regex "Revision|PriorRevision")
  218. file(STRINGS ${TOP}/${UPDATE_XML_FILE} UPDATE_XML_REVISIONS
  219. REGEX "^\t<(${rev_regex})>[^<\n]+</(${rev_regex})>$"
  220. )
  221. set(found_revisons FALSE)
  222. foreach(r IN LISTS UPDATE_XML_REVISIONS)
  223. if("${r}" MATCHES "PriorRevision")
  224. message(FATAL_ERROR "Found PriorRevision in no update test")
  225. endif()
  226. if("${r}" MATCHES "<Revision>")
  227. set(found_revisons TRUE)
  228. endif()
  229. endforeach()
  230. if(found_revisons)
  231. message(" found <Revision> in no update test")
  232. else()
  233. message(FATAL_ERROR " missing <Revision> in no update test")
  234. endif()
  235. endfunction()
  236. #-----------------------------------------------------------------------------
  237. # Function to find the Update.xml file and make sure
  238. # it only has the UpdateReturnStatus failure message and no updates.
  239. function(check_fail_update bin_dir)
  240. set(PATTERN ${TOP}/${bin_dir}/Testing/*/Update.xml)
  241. file(GLOB UPDATE_XML_FILE RELATIVE ${TOP} ${PATTERN})
  242. string(REGEX REPLACE "//Update.xml$" "/Update.xml"
  243. UPDATE_XML_FILE "${UPDATE_XML_FILE}")
  244. message(" found ${UPDATE_XML_FILE}")
  245. file(STRINGS ${TOP}/${UPDATE_XML_FILE} UPDATE_XML_STATUS
  246. REGEX "^\t<UpdateReturnStatus>[^<\n]+"
  247. )
  248. if(UPDATE_XML_STATUS MATCHES "Update command failed")
  249. message(" correctly found 'Update command failed'")
  250. else()
  251. message(FATAL_ERROR " missing 'Update command failed'")
  252. endif()
  253. endfunction()
  254. #-----------------------------------------------------------------------------
  255. # Function to run the dashboard through a script
  256. function(run_dashboard_script bin_dir)
  257. run_child(
  258. WORKING_DIRECTORY ${TOP}
  259. COMMAND ${CMAKE_CTEST_COMMAND} -S ${bin_dir}.cmake -V
  260. )
  261. # Verify the updates reported by CTest.
  262. list(APPEND UPDATE_MAYBE Updated{subdir} Updated{CTestConfig.cmake})
  263. if(NO_UPDATE)
  264. check_no_update(${bin_dir})
  265. elseif(FAIL_UPDATE)
  266. check_fail_update(${bin_dir})
  267. else()
  268. check_updates(${bin_dir}
  269. Updated{foo.txt}
  270. Updated{bar.txt}
  271. Updated{zot.txt}
  272. Updated{subdir/foo.txt}
  273. Updated{subdir/bar.txt}
  274. )
  275. endif()
  276. # Pass console output up to the parent, in case they'd like to inspect it.
  277. set(OUTPUT "${OUTPUT}" PARENT_SCOPE)
  278. endfunction()
  279. #-----------------------------------------------------------------------------
  280. # Function to initialize the testing directory.
  281. function(init_testing)
  282. file(REMOVE_RECURSE ${TOP})
  283. file(MAKE_DIRECTORY ${TOP})
  284. endfunction()