utils.cmake 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. include(${CMAKE_CURRENT_LIST_DIR}/code_generators.cmake)
  2. function(serenity_set_implicit_links target_name)
  3. # Make sure that CMake is aware of the implicit LibC dependency, and ensure
  4. # that we are choosing the correct and updated LibC.
  5. # The latter is a problem with Clang especially, since we might have the
  6. # slightly outdated stub in the sysroot, but have not yet installed the freshly
  7. # built LibC.
  8. target_link_libraries(${target_name} PRIVATE LibC)
  9. endfunction()
  10. function(serenity_install_headers target_name)
  11. file(GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.h")
  12. foreach(header ${headers})
  13. get_filename_component(subdirectory ${header} DIRECTORY)
  14. install(FILES ${header} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${target_name}/${subdirectory}" OPTIONAL)
  15. endforeach()
  16. endfunction()
  17. function(serenity_install_sources)
  18. cmake_path(RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR BASE_DIRECTORY ${SerenityOS_SOURCE_DIR} OUTPUT_VARIABLE current_source_dir_relative)
  19. file(GLOB_RECURSE sources RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.h" "*.cpp" "*.gml")
  20. foreach(source ${sources})
  21. get_filename_component(subdirectory ${source} DIRECTORY)
  22. install(FILES ${source} DESTINATION usr/src/serenity/${current_source_dir_relative}/${subdirectory} OPTIONAL)
  23. endforeach()
  24. endfunction()
  25. function(serenity_generated_sources target_name)
  26. if(DEFINED GENERATED_SOURCES)
  27. set_source_files_properties(${GENERATED_SOURCES} PROPERTIES GENERATED 1)
  28. foreach(generated ${GENERATED_SOURCES})
  29. get_filename_component(generated_name ${generated} NAME)
  30. add_dependencies(${target_name} generate_${generated_name})
  31. add_dependencies(all_generated generate_${generated_name})
  32. endforeach()
  33. endif()
  34. endfunction()
  35. if (NOT COMMAND serenity_lib)
  36. function(serenity_lib target_name fs_name)
  37. cmake_parse_arguments(PARSE_ARGV 2 SERENITY_LIB "" "TYPE" "")
  38. if ("${SERENITY_LIB_TYPE}" STREQUAL "")
  39. set(SERENITY_LIB_TYPE SHARED)
  40. endif()
  41. serenity_install_headers(${target_name})
  42. serenity_install_sources()
  43. add_library(${target_name} ${SERENITY_LIB_TYPE} ${SOURCES} ${GENERATED_SOURCES})
  44. set_target_properties(${target_name} PROPERTIES EXCLUDE_FROM_ALL TRUE)
  45. set_target_properties(${target_name} PROPERTIES VERSION "serenity")
  46. target_link_libraries(${target_name} PUBLIC GenericClangPlugin)
  47. install(TARGETS ${target_name} DESTINATION ${CMAKE_INSTALL_LIBDIR} OPTIONAL)
  48. set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${fs_name})
  49. serenity_generated_sources(${target_name})
  50. serenity_set_implicit_links(${target_name})
  51. endfunction()
  52. endif()
  53. function(serenity_libc target_name fs_name)
  54. serenity_install_headers("")
  55. serenity_install_sources()
  56. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib -fpic -ftls-model=initial-exec")
  57. add_library(${target_name} SHARED ${SOURCES})
  58. install(TARGETS ${target_name} DESTINATION ${CMAKE_INSTALL_LIBDIR})
  59. set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${fs_name})
  60. # Avoid creating a dependency cycle between system libraries and the C++ standard library. This is necessary
  61. # to ensure that initialization functions will be called in the right order (libc++ must come after LibPthread).
  62. target_link_options(${target_name} PRIVATE -static-libstdc++)
  63. if (CMAKE_CXX_COMPILER_ID MATCHES "Clang$" AND ENABLE_USERSPACE_COVERAGE_COLLECTION)
  64. target_link_libraries(${target_name} PRIVATE clang_rt.profile)
  65. endif()
  66. target_link_directories(LibC PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
  67. serenity_generated_sources(${target_name})
  68. endfunction()
  69. if (NOT COMMAND serenity_bin)
  70. function(serenity_bin target_name)
  71. serenity_install_sources()
  72. add_executable(${target_name} ${SOURCES})
  73. target_link_libraries(${target_name} PUBLIC GenericClangPlugin)
  74. set_target_properties(${target_name} PROPERTIES EXCLUDE_FROM_ALL TRUE)
  75. install(TARGETS ${target_name} RUNTIME DESTINATION bin OPTIONAL)
  76. serenity_generated_sources(${target_name})
  77. serenity_set_implicit_links(${target_name})
  78. endfunction()
  79. endif()
  80. if (NOT COMMAND serenity_test)
  81. function(serenity_test test_src sub_dir)
  82. cmake_parse_arguments(PARSE_ARGV 2 SERENITY_TEST "MAIN_ALREADY_DEFINED" "CUSTOM_MAIN" "LIBS")
  83. set(TEST_SOURCES ${test_src})
  84. if ("${SERENITY_TEST_CUSTOM_MAIN}" STREQUAL "")
  85. set(SERENITY_TEST_CUSTOM_MAIN "$<TARGET_OBJECTS:LibTestMain>")
  86. endif()
  87. if (NOT ${SERENITY_TEST_MAIN_ALREADY_DEFINED})
  88. list(PREPEND TEST_SOURCES "${SERENITY_TEST_CUSTOM_MAIN}")
  89. endif()
  90. get_filename_component(test_name ${test_src} NAME_WE)
  91. add_executable(${test_name} ${TEST_SOURCES})
  92. add_dependencies(ComponentTests ${test_name})
  93. set_target_properties(${test_name} PROPERTIES EXCLUDE_FROM_ALL TRUE)
  94. serenity_set_implicit_links(${test_name})
  95. target_link_libraries(${test_name} PRIVATE LibTest LibCore LibFileSystem)
  96. foreach(lib ${SERENITY_TEST_LIBS})
  97. target_link_libraries(${test_name} PRIVATE ${lib})
  98. endforeach()
  99. install(TARGETS ${test_name} RUNTIME DESTINATION usr/Tests/${sub_dir} OPTIONAL)
  100. endfunction()
  101. endif()
  102. function(serenity_testjs_test test_src sub_dir)
  103. cmake_parse_arguments(PARSE_ARGV 2 SERENITY_TEST "" "CUSTOM_MAIN" "LIBS")
  104. if ("${SERENITY_TEST_CUSTOM_MAIN}" STREQUAL "")
  105. set(SERENITY_TEST_CUSTOM_MAIN "$<TARGET_OBJECTS:JavaScriptTestRunnerMain>")
  106. endif()
  107. list(APPEND SERENITY_TEST_LIBS LibJS LibCore LibFileSystem)
  108. serenity_test(${test_src} ${sub_dir}
  109. CUSTOM_MAIN "${SERENITY_TEST_CUSTOM_MAIN}"
  110. LIBS ${SERENITY_TEST_LIBS})
  111. endfunction()
  112. function(serenity_app target_name)
  113. cmake_parse_arguments(PARSE_ARGV 1 SERENITY_APP "" "ICON" "")
  114. serenity_bin("${target_name}")
  115. set(small_icon "${SerenityOS_SOURCE_DIR}/Base/res/icons/16x16/${SERENITY_APP_ICON}.png")
  116. set(medium_icon "${SerenityOS_SOURCE_DIR}/Base/res/icons/32x32/${SERENITY_APP_ICON}.png")
  117. if (EXISTS "${small_icon}")
  118. embed_resource("${target_name}" serenity_icon_s "${small_icon}")
  119. else()
  120. message(FATAL_ERROR "Missing small app icon: ${small_icon}")
  121. endif()
  122. if (EXISTS "${medium_icon}")
  123. embed_resource("${target_name}" serenity_icon_m "${medium_icon}")
  124. else()
  125. # These icons are designed small only for use in applets, and thus are exempt.
  126. list(APPEND allowed_missing_medium_icons "audio-volume-high")
  127. list(APPEND allowed_missing_medium_icons "edit-copy")
  128. if (NOT ${SERENITY_APP_ICON} IN_LIST allowed_missing_medium_icons)
  129. message(FATAL_ERROR "Missing medium app icon: ${medium_icon}")
  130. endif()
  131. endif()
  132. endfunction()
  133. function(remove_path_if_version_changed version version_file cache_path)
  134. set(version_differs YES)
  135. if (EXISTS "${version_file}")
  136. file(STRINGS "${version_file}" active_version)
  137. if (version STREQUAL active_version)
  138. set(version_differs NO)
  139. endif()
  140. endif()
  141. if (version_differs)
  142. message(STATUS "Removing outdated ${cache_path} for version ${version}")
  143. file(REMOVE_RECURSE "${cache_path}")
  144. file(WRITE "${version_file}" "${version}")
  145. endif()
  146. endfunction()
  147. function(invoke_generator name generator primary_source header implementation)
  148. cmake_parse_arguments(invoke_generator "" "" "arguments;dependencies" ${ARGN})
  149. add_custom_command(
  150. OUTPUT "${header}" "${implementation}"
  151. COMMAND $<TARGET_FILE:${generator}> -h "${header}.tmp" -c "${implementation}.tmp" ${invoke_generator_arguments}
  152. COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${header}.tmp" "${header}"
  153. COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${implementation}.tmp" "${implementation}"
  154. COMMAND "${CMAKE_COMMAND}" -E remove "${header}.tmp" "${implementation}.tmp"
  155. VERBATIM
  156. DEPENDS ${generator} ${invoke_generator_dependencies} "${primary_source}"
  157. )
  158. add_custom_target("generate_${name}" DEPENDS "${header}" "${implementation}")
  159. add_dependencies(all_generated "generate_${name}")
  160. list(APPEND CURRENT_LIB_GENERATED "${name}")
  161. set(CURRENT_LIB_GENERATED ${CURRENT_LIB_GENERATED} PARENT_SCOPE)
  162. endfunction()
  163. function(download_file_multisource urls path)
  164. cmake_parse_arguments(DOWNLOAD "" "SHA256" "" ${ARGN})
  165. if (NOT "${DOWNLOAD_SHA256}" STREQUAL "")
  166. set(DOWNLOAD_SHA256 EXPECTED_HASH "SHA256=${DOWNLOAD_SHA256}")
  167. endif()
  168. if (NOT EXISTS "${path}")
  169. if (NOT ENABLE_NETWORK_DOWNLOADS)
  170. message(FATAL_ERROR "${path} does not exist, and unable to download it")
  171. endif()
  172. get_filename_component(file "${path}" NAME)
  173. set(tmp_path "${path}.tmp")
  174. foreach(url ${urls})
  175. message(STATUS "Downloading file ${file} from ${url}")
  176. file(DOWNLOAD "${url}" "${tmp_path}" INACTIVITY_TIMEOUT 10 STATUS download_result ${DOWNLOAD_SHA256})
  177. list(GET download_result 0 status_code)
  178. list(GET download_result 1 error_message)
  179. if (status_code EQUAL 0)
  180. file(RENAME "${tmp_path}" "${path}")
  181. break()
  182. endif()
  183. file(REMOVE "${tmp_path}")
  184. message(WARNING "Failed to download ${url}: ${error_message}")
  185. endforeach()
  186. if (NOT status_code EQUAL 0)
  187. message(FATAL_ERROR "Failed to download ${path} from any source")
  188. endif()
  189. endif()
  190. endfunction()
  191. function(download_file url path)
  192. cmake_parse_arguments(DOWNLOAD "" "SHA256" "" ${ARGN})
  193. # If the timestamp doesn't match exactly, the Web Archive should redirect to the closest archived file automatically.
  194. download_file_multisource("${url};https://web.archive.org/web/99991231235959/${url}" "${path}" SHA256 "${DOWNLOAD_SHA256}")
  195. endfunction()
  196. function(extract_path dest_dir zip_path source_path dest_path)
  197. if (EXISTS "${zip_path}" AND NOT EXISTS "${dest_path}")
  198. file(ARCHIVE_EXTRACT INPUT "${zip_path}" DESTINATION "${dest_dir}" PATTERNS "${source_path}")
  199. endif()
  200. endfunction()
  201. function(add_lagom_library_install_rules target_name)
  202. cmake_parse_arguments(PARSE_ARGV 1 LAGOM_INSTALL_RULES "" "ALIAS_NAME" "")
  203. if (NOT LAGOM_INSTALL_RULES_ALIAS_NAME)
  204. set(LAGOM_INSTALL_RULES_ALIAS_NAME ${target_name})
  205. endif()
  206. # Don't make alias when we're going to import a previous build for Tools
  207. # FIXME: Is there a better way to write this?
  208. if (NOT ENABLE_FUZZERS AND NOT CMAKE_CROSSCOMPILING)
  209. # alias for parity with exports
  210. add_library(Lagom::${LAGOM_INSTALL_RULES_ALIAS_NAME} ALIAS ${target_name})
  211. endif()
  212. install(TARGETS ${target_name} EXPORT LagomTargets
  213. RUNTIME COMPONENT Lagom_Runtime
  214. LIBRARY COMPONENT Lagom_Runtime NAMELINK_COMPONENT Lagom_Development
  215. ARCHIVE COMPONENT Lagom_Development
  216. INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
  217. )
  218. endfunction()
  219. if (NOT COMMAND swizzle_target_properties_for_swift)
  220. function(swizzle_target_properties_for_swift target)
  221. endfunction()
  222. endif()