check-json.cmake 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. cmake_policy(PUSH)
  2. cmake_policy(SET CMP0057 NEW)
  3. function (json_placeholders in out)
  4. string(REPLACE "<CONFIG>" "${CXXModules_config}" in "${in}")
  5. string(TOLOWER "${CXXModules_config}" config_lower)
  6. string(REPLACE "<CONFIG_LOWER>" "${config_lower}" in "${in}")
  7. string(REPLACE "<CONFIG_OTHER>" "${CXXModules_config_other}" in "${in}")
  8. string(TOLOWER "${CXXModules_config_other}" config_lower)
  9. string(REPLACE "<CONFIG_OTHER_LOWER>" "${config_lower}" in "${in}")
  10. if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
  11. string(REPLACE "<CONFIG_DIR>" "/${CXXModules_config}" in "${in}")
  12. string(REPLACE "<CONFIG_OTHER_DIR>" "/${CXXModules_config_other}" in "${in}")
  13. else ()
  14. string(REPLACE "<CONFIG_DIR>" "" in "${in}")
  15. string(REPLACE "<CONFIG_OTHER_DIR>" "" in "${in}")
  16. endif ()
  17. if (CMAKE_BUILD_TYPE)
  18. string(REPLACE "<CONFIG_FORCE>" "${CXXModules_config}" in "${in}")
  19. string(REPLACE "<CONFIG_OTHER_FORCE>" "${CXXModules_config_other}" in "${in}")
  20. else ()
  21. string(REPLACE "<CONFIG_FORCE>" "noconfig" in "${in}")
  22. endif ()
  23. string(REPLACE "<SOURCE_DIR>" "${RunCMake_SOURCE_DIR}" in "${in}")
  24. string(REPLACE "<BINARY_DIR>" "${RunCMake_TEST_BINARY_DIR}" in "${in}")
  25. string(REPLACE "<OBJEXT>" "${CMAKE_CXX_OUTPUT_EXTENSION}" in "${in}")
  26. if (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "gcc")
  27. set(bmiflag "-fmodule-only")
  28. set(bmiext "gcm")
  29. elseif (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "clang")
  30. set(bmiflag "--precompile")
  31. set(bmiext "pcm")
  32. elseif (CMAKE_CXX_MODULE_MAP_FORMAT STREQUAL "msvc")
  33. set(bmiflag "-ifcOutput.*")
  34. set(bmiext "ifc")
  35. endif ()
  36. string(REPLACE "<BMI_ONLY_FLAG>" "${bmiflag}" in "${in}")
  37. string(REPLACE "<BMIEXT>" ".${bmiext}" in "${in}")
  38. set(output_flag "-o")
  39. if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
  40. set(output_flag "-Fo")
  41. endif ()
  42. string(REPLACE "<OUTPUT_FLAG>" "${output_flag}" in "${in}")
  43. string(REPLACE "<CXX20_OPTION>" "${CMAKE_CXX20_STANDARD_COMPILE_OPTION}" in "${in}")
  44. string(REPLACE "<HEX>" "[0-9a-f]+" in "${in}")
  45. string(REPLACE "REGEX:" "" in "${in}")
  46. string(REPLACE "PATH:" "" in "${in}")
  47. set("${out}" "${in}" PARENT_SCOPE)
  48. endfunction ()
  49. function (check_json_value path actual_type expect_type actual_value expect_value)
  50. if (NOT actual_type STREQUAL expect_type)
  51. list(APPEND RunCMake_TEST_FAILED
  52. "Type mismatch at ${path}: ${actual_type} vs. ${expect_type}")
  53. set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
  54. return ()
  55. endif ()
  56. if (actual_type STREQUAL NULL)
  57. # Nothing to check
  58. elseif (actual_type STREQUAL BOOLEAN)
  59. if (NOT actual_value STREQUAL expect_value)
  60. list(APPEND RunCMake_TEST_FAILED
  61. "Boolean mismatch at ${path}: ${actual_value} vs. ${expect_value}")
  62. endif ()
  63. elseif (actual_type STREQUAL NUMBER)
  64. if (NOT actual_value EQUAL expect_value)
  65. list(APPEND RunCMake_TEST_FAILED
  66. "Number mismatch at ${path}: ${actual_value} vs. ${expect_value}")
  67. endif ()
  68. elseif (actual_type STREQUAL STRING)
  69. # Allow some values to be ignored.
  70. if (expect_value STREQUAL "<IGNORE>")
  71. return ()
  72. endif ()
  73. json_placeholders("${expect_value}" expect_value_expanded)
  74. if (expect_value MATCHES "^REGEX:PATH:")
  75. string(REPLACE "\\" "/" actual_value_check "${actual_value}")
  76. string(REGEX REPLACE "^\"(.*)\"$" "\\1" actual_value_check "${actual_value_check}")
  77. if (NOT actual_value_check MATCHES "^${expect_value_expanded}$")
  78. list(APPEND RunCMake_TEST_FAILED
  79. "String mismatch (path regex) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$")
  80. endif ()
  81. elseif (expect_value MATCHES "^REGEX:")
  82. if (NOT actual_value MATCHES "^${expect_value_expanded}$")
  83. list(APPEND RunCMake_TEST_FAILED
  84. "String mismatch (regex) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$")
  85. endif ()
  86. elseif (expect_value MATCHES "^PATH:")
  87. string(REPLACE "\\" "/" actual_value_check "${actual_value}")
  88. string(REGEX REPLACE "^\"(.*)\"$" "\\1" actual_value_check "${actual_value_check}")
  89. if (NOT actual_value_check STREQUAL "${expect_value_expanded}")
  90. list(APPEND RunCMake_TEST_FAILED
  91. "String mismatch (path) at ${path}: ${actual_value} vs. ^${expect_value_expanded}$")
  92. endif ()
  93. elseif (NOT actual_value STREQUAL expect_value_expanded)
  94. list(APPEND RunCMake_TEST_FAILED
  95. "String mismatch at ${path}: ${actual_value} vs. ${expect_value_expanded}")
  96. endif ()
  97. elseif (actual_type STREQUAL ARRAY)
  98. check_json_array("${path}" "${actual_value}" "${expect_value}")
  99. elseif (actual_type STREQUAL OBJECT)
  100. check_json_object("${path}" "${actual_value}" "${expect_value}")
  101. endif ()
  102. set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
  103. endfunction ()
  104. # Check that two arrays are the same.
  105. function (check_json_array path actual expect)
  106. if (item_filter)
  107. string(JSON iter_len LENGTH "${actual}")
  108. set(idx 0)
  109. while (idx LESS iter_len)
  110. string(JSON type TYPE "${actual}" "${idx}")
  111. string(JSON item GET "${actual}" "${idx}")
  112. if (type STREQUAL "STRING" AND
  113. item MATCHES "${item_filter}")
  114. string(JSON actual REMOVE "${actual}" "${idx}")
  115. math(EXPR iter_len "${iter_len} - 1")
  116. else ()
  117. math(EXPR idx "${idx} + 1")
  118. endif ()
  119. endwhile ()
  120. endif ()
  121. string(JSON actual_len LENGTH "${actual}")
  122. string(JSON expect_len LENGTH "${expect}")
  123. set(iter_len "${actual_len}")
  124. if (actual_len LESS expect_len)
  125. list(APPEND RunCMake_TEST_FAILED
  126. "Missing array items at ${path}")
  127. elseif (expect_len LESS actual_len)
  128. list(APPEND RunCMake_TEST_FAILED
  129. "Extra array items at ${path}")
  130. set(iter_len "${expect_len}")
  131. endif ()
  132. foreach (idx RANGE "${iter_len}")
  133. if (idx EQUAL iter_len)
  134. break ()
  135. endif ()
  136. set(new_path "${path}[${idx}]")
  137. string(JSON actual_type TYPE "${actual}" "${idx}")
  138. string(JSON expect_type TYPE "${expect}" "${idx}")
  139. string(JSON actual_value GET "${actual}" "${idx}")
  140. string(JSON expect_value GET "${expect}" "${idx}")
  141. check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
  142. endforeach ()
  143. set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
  144. endfunction ()
  145. # Check that two inner objects are the same.
  146. function (check_json_object path actual expect)
  147. string(JSON actual_len LENGTH "${actual}")
  148. string(JSON expect_len LENGTH "${expect}")
  149. set(actual_keys "")
  150. set(expect_keys "")
  151. foreach (idx RANGE "${actual_len}")
  152. if (idx EQUAL actual_len)
  153. break ()
  154. endif ()
  155. string(JSON actual_key MEMBER "${actual}" "${idx}")
  156. list(APPEND actual_keys "${actual_key}")
  157. endforeach ()
  158. foreach (idx RANGE "${expect_len}")
  159. if (idx EQUAL expect_len)
  160. break ()
  161. endif ()
  162. string(JSON expect_key MEMBER "${expect}" "${idx}")
  163. list(APPEND expect_keys "${expect_key}")
  164. endforeach ()
  165. json_placeholders("${expect_keys}" expect_keys_expanded)
  166. set(actual_keys_missed "${actual_keys}")
  167. set(expect_keys_missed "${expect_keys}")
  168. set(common_keys "")
  169. set(expect_keys_stack "${expect_keys}")
  170. while (expect_keys_stack)
  171. list(POP_BACK expect_keys_stack expect_key)
  172. json_placeholders("${expect_key}" expect_key_expanded)
  173. if (expect_key_expanded IN_LIST actual_keys_missed AND
  174. expect_key IN_LIST expect_keys_missed)
  175. list(APPEND common_keys "${expect_key}")
  176. endif ()
  177. list(REMOVE_ITEM actual_keys_missed "${expect_key_expanded}")
  178. list(REMOVE_ITEM expect_keys_missed "${expect_key}")
  179. endwhile ()
  180. if (actual_keys_missed)
  181. string(REPLACE ";" ", " actual_keys_missed_text "${actual_keys_missed}")
  182. list(APPEND RunCMake_TEST_FAILED
  183. "Extra unexpected members at ${path}: ${actual_keys_missed_text}")
  184. endif ()
  185. if (expect_keys_missed)
  186. string(REPLACE ";" ", " expect_keys_missed_text "${expect_keys_missed}")
  187. list(APPEND RunCMake_TEST_FAILED
  188. "Missing expected members at ${path}: ${expect_keys_missed_text}")
  189. endif ()
  190. foreach (key IN LISTS common_keys)
  191. json_placeholders("${key}" key_expanded)
  192. set(new_path "${path}.${key_expanded}")
  193. string(JSON actual_type TYPE "${actual}" "${key_expanded}")
  194. string(JSON expect_type TYPE "${expect}" "${key}")
  195. string(JSON actual_value GET "${actual}" "${key_expanded}")
  196. string(JSON expect_value GET "${expect}" "${key}")
  197. check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
  198. endforeach ()
  199. set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
  200. endfunction ()
  201. # Check that two JSON objects are the same.
  202. function (check_json actual expect)
  203. check_json_object("" "${actual}" "${expect}")
  204. set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
  205. endfunction ()
  206. cmake_policy(POP)