BuildMetalib.cmake 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # Filename: BuildMetalib.cmake
  2. #
  3. # Description: This file contains macros to build Panda3D's "metalibs" - these
  4. # are special libraries that contain no unique code themselves and are
  5. # instead just an agglomeration of the various component libraries that get
  6. # linked into them. A library of libraries - a "metalibrary."
  7. #
  8. # Function: target_link_libraries(...)
  9. #
  10. # Overrides CMake's target_link_libraries() to support "linking" object
  11. # libraries. This is a partial reimplementation of CMake commit dc38970f83,
  12. # which as of this writing has not yet landed in any release.
  13. #
  14. function(target_link_libraries target)
  15. get_target_property(target_type "${target}" TYPE)
  16. if(NOT target_type STREQUAL "OBJECT_LIBRARY")
  17. _target_link_libraries("${target}" ${ARGN})
  18. return()
  19. endif()
  20. foreach(library ${ARGN})
  21. # This is a quick and dirty regex to tell targets apart from other stuff.
  22. # It just checks if it's alphanumeric and starts with p3/panda.
  23. if(library MATCHES "^(p3|panda)[A-Za-z0-9]*$")
  24. # We need to add "library"'s include directories to "target"
  25. # (and transitively to INTERFACE_INCLUDE_DIRECTORIES so further
  26. # dependencies will work)
  27. set(include_directories "$<TARGET_PROPERTY:${library},INTERFACE_INCLUDE_DIRECTORIES>")
  28. set_property(TARGET "${target}" APPEND PROPERTY INCLUDE_DIRECTORIES "${include_directories}")
  29. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${include_directories}")
  30. # Also build with the same BUILDING_ macros, because these will all end
  31. # up in the same library.
  32. set(compile_definitions "$<$<BOOL:$<TARGET_PROPERTY:${library},IS_COMPONENT>>:$<TARGET_PROPERTY:${library},COMPILE_DEFINITIONS>>")
  33. set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}")
  34. # Libraries are only linked transitively if they aren't components.
  35. # Unfortunately, it seems like INTERFACE_LINK_LIBRARIES can't have
  36. # generator expressions on an object library(?) so we resort to taking
  37. # care of this at configuration time.
  38. if(TARGET "${library}")
  39. get_target_property(is_component "${library}" IS_COMPONENT)
  40. else()
  41. # This is a safe assumption, since we define all component libraries
  42. # before the metalib they appear in:
  43. set(is_component OFF)
  44. endif()
  45. if(NOT is_component)
  46. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
  47. endif()
  48. else()
  49. # This is a file path to an out-of-tree library - this needs to be
  50. # recorded so that the metalib can link them. (They aren't needed at
  51. # all for the object libraries themselves, so they don't have to work
  52. # transitively.)
  53. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
  54. endif()
  55. endforeach()
  56. endfunction(target_link_libraries)
  57. #
  58. # Function: add_component_library(target [SYMBOL building_symbol]
  59. # [SOURCES] [[NOINIT]/[INIT func [header]]])
  60. #
  61. # Used very similarly to add_library. You can specify a symbol with SYMBOL,
  62. # which works like CMake's own DEFINE_SYMBOL property: it's defined when
  63. # building the library, but not when building something that links against the
  64. # library.
  65. #
  66. # INIT specifies the init function that should be called from a metalib's init
  67. # function when this is added to a metalib. The header parameter can further
  68. # clarify what header declares this function. By default, this is
  69. # init_libTARGET and config_TARGET.h, respectively, where TARGET is the
  70. # target name (with 'p3' stripped off, if applicable). The NOINIT keyword
  71. # suppresses this default.
  72. #
  73. # Note that this function gets to decide whether the component library is
  74. # OBJECT or SHARED, and whether the library is installed or not. Also, as
  75. # a rule, component libraries may only be linked by other component libraries
  76. # in the same metalib - outside of the metalib, you must link the metalib
  77. # itself.
  78. #
  79. function(add_component_library target_name)
  80. set(sources)
  81. unset(symbol)
  82. if(target_name MATCHES "^p3.*")
  83. string(SUBSTRING "${target_name}" 2 -1 name_without_prefix)
  84. else()
  85. set(name_without_prefix "${target_name}")
  86. endif()
  87. set(init_func "init_lib${name_without_prefix}")
  88. set(init_header "config_${name_without_prefix}.h")
  89. set(symbol_keyword OFF)
  90. set(init_keyword 0)
  91. foreach(source ${ARGN})
  92. if(source STREQUAL "SYMBOL")
  93. set(symbol_keyword ON)
  94. set(init_keyword 0)
  95. elseif(source STREQUAL "INIT")
  96. set(symbol_keyword OFF)
  97. set(init_keyword 2)
  98. elseif(source STREQUAL "NOINIT")
  99. set(init_func)
  100. set(init_header)
  101. elseif(symbol_keyword)
  102. set(symbol_keyword OFF)
  103. set(symbol "${source}")
  104. elseif(init_keyword EQUAL 2)
  105. set(init_func "${source}")
  106. set(init_keyword 1)
  107. elseif(init_keyword EQUAL 1)
  108. set(init_header "${source}")
  109. set(init_keyword 0)
  110. else()
  111. list(APPEND sources "${source}")
  112. endif()
  113. endforeach()
  114. if(BUILD_METALIBS)
  115. add_library("${target_name}" OBJECT ${sources})
  116. else()
  117. add_library("${target_name}" ${sources})
  118. endif()
  119. set_target_properties("${target_name}" PROPERTIES
  120. IS_COMPONENT ON
  121. INIT_FUNCTION "${init_func}"
  122. INIT_HEADER "${init_header}")
  123. if(symbol)
  124. # ... DEFINE_SYMBOL is apparently not respected for object libraries?
  125. set_property(TARGET "${target_name}" APPEND PROPERTY COMPILE_DEFINITIONS "${symbol}")
  126. endif()
  127. if(BUILD_METALIBS)
  128. # Apparently neither is CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE?
  129. set_property(TARGET "${target_name}" PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}")
  130. # If we're building dynamic libraries, the object library needs to be -fPIC
  131. if(BUILD_SHARED_LIBS)
  132. set_property(TARGET "${target_name}" PROPERTY POSITION_INDEPENDENT_CODE ON)
  133. endif()
  134. endif()
  135. endfunction(add_component_library)
  136. #
  137. # Function: add_metalib(target [source1 source2] [INIT initfunc [initheader.h]]
  138. # [COMPONENTS component1 ...])
  139. #
  140. # This is add_library, but for metalibs.
  141. #
  142. # The INIT keyword can specify an initialization function/header (which will be
  143. # autogenerated by this function) that calls the underlying component libs'
  144. # init functions.
  145. #
  146. function(add_metalib target_name)
  147. set(components_keyword OFF)
  148. set(init_keyword 0)
  149. set(init_func)
  150. set(init_header "${target_name}.h")
  151. set(components)
  152. set(sources)
  153. foreach(arg ${ARGN})
  154. if(arg STREQUAL "COMPONENTS")
  155. set(components_keyword ON)
  156. set(init_keyword 0)
  157. elseif(arg STREQUAL "INIT")
  158. set(init_keyword 2)
  159. set(components_keyword OFF)
  160. elseif(components_keyword)
  161. list(APPEND components "${arg}")
  162. elseif(init_keyword EQUAL 2)
  163. set(init_func "${arg}")
  164. set(init_keyword 1)
  165. elseif(init_keyword EQUAL 1)
  166. set(init_header "${arg}")
  167. set(init_keyword 0)
  168. else()
  169. list(APPEND sources "${arg}")
  170. endif()
  171. endforeach()
  172. set(defines)
  173. set(includes)
  174. set(libs)
  175. set(component_init_funcs "")
  176. set(component_init_headers "")
  177. foreach(component ${components})
  178. if(NOT TARGET "${component}")
  179. message(FATAL_ERROR
  180. "Missing component library ${component} referenced by metalib ${target_name}!
  181. (Component library targets must be created BEFORE add_metalib.)")
  182. endif()
  183. get_target_property(is_component "${component}" IS_COMPONENT)
  184. if(NOT is_component)
  185. message(FATAL_ERROR
  186. "Attempted to metalink non-component ${component} into ${target_name}!")
  187. endif()
  188. get_target_property(component_init_header "${component}" INIT_HEADER)
  189. get_target_property(component_init_func "${component}" INIT_FUNCTION)
  190. if(component_init_header)
  191. set(component_init_headers
  192. "${component_init_headers}#include \"${component_init_header}\"\n")
  193. endif()
  194. if(component_init_func)
  195. set(component_init_funcs
  196. "${component_init_funcs} ${component_init_func}();\n")
  197. endif()
  198. if(BUILD_METALIBS)
  199. list(APPEND defines "$<TARGET_PROPERTY:${component},COMPILE_DEFINITIONS>")
  200. list(APPEND includes "$<TARGET_PROPERTY:${component},INTERFACE_INCLUDE_DIRECTORIES>")
  201. list(APPEND libs "$<TARGET_PROPERTY:${component},INTERFACE_LINK_LIBRARIES>")
  202. list(APPEND sources "$<TARGET_OBJECTS:${component}>")
  203. else()
  204. list(APPEND libs "${component}")
  205. endif()
  206. endforeach()
  207. if(init_func)
  208. set(init_source_path "${CMAKE_CURRENT_BINARY_DIR}/init_${target_name}.cxx")
  209. set(init_header_path "${CMAKE_CURRENT_BINARY_DIR}/${init_header}")
  210. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.cxx.in" "${init_source_path}")
  211. list(APPEND sources "${init_source_path}")
  212. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.h.in" "${init_header_path}")
  213. install(FILES "${init_header_path}" DESTINATION include/panda3d)
  214. endif()
  215. add_library("${target_name}" ${sources})
  216. target_compile_definitions("${target_name}" PRIVATE ${defines})
  217. target_link_libraries("${target_name}" ${libs})
  218. target_include_directories("${target_name}" PUBLIC ${includes})
  219. endfunction(add_metalib)