BuildMetalib.cmake 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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 is only available in CMake 3.12+
  13. #
  14. if(CMAKE_VERSION VERSION_LESS "3.12")
  15. function(target_link_libraries target)
  16. get_target_property(target_type "${target}" TYPE)
  17. if(NOT target_type STREQUAL "OBJECT_LIBRARY")
  18. _target_link_libraries("${target}" ${ARGN})
  19. return()
  20. endif()
  21. foreach(library ${ARGN})
  22. # This is a quick and dirty regex to tell targets apart from other stuff.
  23. # It just checks if it's alphanumeric and starts with p3/panda.
  24. if(library MATCHES "^(PKG::|p3|panda)[A-Za-z0-9]*$")
  25. # We need to add "library"'s include directories to "target"
  26. # (and transitively to INTERFACE_INCLUDE_DIRECTORIES so further
  27. # dependencies will work)
  28. set(include_directories "$<TARGET_PROPERTY:${library},INTERFACE_INCLUDE_DIRECTORIES>")
  29. set_property(TARGET "${target}" APPEND PROPERTY INCLUDE_DIRECTORIES "${include_directories}")
  30. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${include_directories}")
  31. # And for INTERFACE_COMPILE_DEFINITIONS as well
  32. set(compile_definitions "$<TARGET_PROPERTY:${library},INTERFACE_COMPILE_DEFINITIONS>")
  33. set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}")
  34. # Build up some generator expressions for determining whether `library`
  35. # is a component library or not.
  36. if(library MATCHES ".*::.*")
  37. # "::" messes up CMake's genex parser; fortunately, a library whose
  38. # name contains that is either an interface library or alias, and
  39. # definitely not a component
  40. set(is_component 0)
  41. set(name_of_component "")
  42. set(name_of_non_component "${library}")
  43. else()
  44. set(is_component "$<TARGET_PROPERTY:${library},IS_COMPONENT>")
  45. # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :(
  46. set(is_object "$<STREQUAL:$<TARGET_PROPERTY:${library},TYPE>,OBJECT_LIBRARY>")
  47. set(is_component "$<BOOL:$<${is_object}:${is_component}>>")
  48. set(name_of_component "$<${is_component}:${library}>")
  49. set(name_of_non_component "$<$<NOT:${is_component}>:${library}>")
  50. endif()
  51. # Libraries are only linked transitively if they aren't components.
  52. set_property(TARGET "${target}" APPEND PROPERTY
  53. INTERFACE_LINK_LIBRARIES "${name_of_non_component}")
  54. # Also build with the same BUILDING_ macros, because these will all end
  55. # up in the same library.
  56. set(compile_definitions "$<TARGET_PROPERTY:${library},COMPILE_DEFINITIONS>")
  57. set_property(TARGET "${target}" APPEND PROPERTY
  58. COMPILE_DEFINITIONS "$<${is_component}:${compile_definitions}>")
  59. else()
  60. # This is a file path to an out-of-tree library - this needs to be
  61. # recorded so that the metalib can link them. (They aren't needed at
  62. # all for the object libraries themselves, so they don't have to work
  63. # transitively.)
  64. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
  65. endif()
  66. endforeach(library)
  67. endfunction(target_link_libraries)
  68. endif()
  69. #
  70. # Function: add_component_library(target [SYMBOL building_symbol]
  71. # [SOURCES] [[NOINIT]/[INIT func [header]]])
  72. #
  73. # Used very similarly to add_library. You can specify a symbol with SYMBOL,
  74. # which works like CMake's own DEFINE_SYMBOL property: it's defined when
  75. # building the library, but not when building something that links against the
  76. # library.
  77. #
  78. # INIT specifies the init function that should be called from a metalib's init
  79. # function when this is added to a metalib. The header parameter can further
  80. # clarify what header declares this function. By default, this is
  81. # init_libTARGET and config_TARGET.h, respectively, where TARGET is the
  82. # target name (with 'p3' stripped off, if applicable). The NOINIT keyword
  83. # suppresses this default.
  84. #
  85. # Note that this function gets to decide whether the component library is
  86. # OBJECT or SHARED, and whether the library is installed or not. Also, as
  87. # a rule, component libraries may only be linked by other component libraries
  88. # in the same metalib - outside of the metalib, you must link the metalib
  89. # itself.
  90. #
  91. function(add_component_library target_name)
  92. set(sources)
  93. unset(symbol)
  94. if(target_name MATCHES "^p3.*")
  95. string(SUBSTRING "${target_name}" 2 -1 name_without_prefix)
  96. else()
  97. set(name_without_prefix "${target_name}")
  98. endif()
  99. set(init_func "init_lib${name_without_prefix}")
  100. set(init_header "config_${name_without_prefix}.h")
  101. set(symbol_keyword OFF)
  102. set(init_keyword 0)
  103. foreach(source ${ARGN})
  104. if(source STREQUAL "SYMBOL")
  105. set(symbol_keyword ON)
  106. set(init_keyword 0)
  107. elseif(source STREQUAL "INIT")
  108. set(symbol_keyword OFF)
  109. set(init_keyword 2)
  110. elseif(source STREQUAL "NOINIT")
  111. set(init_func)
  112. set(init_header)
  113. elseif(symbol_keyword)
  114. set(symbol_keyword OFF)
  115. set(symbol "${source}")
  116. elseif(init_keyword EQUAL 2)
  117. set(init_func "${source}")
  118. set(init_keyword 1)
  119. elseif(init_keyword EQUAL 1)
  120. set(init_header "${source}")
  121. set(init_keyword 0)
  122. else()
  123. list(APPEND sources "${source}")
  124. endif()
  125. endforeach()
  126. if(BUILD_METALIBS)
  127. # CMake 3.0.2 doesn't like .I/.N/.T files! We let it know that they're only
  128. # headers.
  129. foreach(source ${sources})
  130. if(source MATCHES "\\.[INT]$")
  131. set_source_files_properties(${source} PROPERTIES HEADER_FILE_ONLY ON)
  132. endif()
  133. endforeach(source)
  134. add_library("${target_name}" OBJECT ${sources})
  135. else()
  136. add_library("${target_name}" ${sources})
  137. endif()
  138. set_target_properties("${target_name}" PROPERTIES
  139. IS_COMPONENT ON
  140. INIT_FUNCTION "${init_func}"
  141. INIT_HEADER "${init_header}")
  142. if(symbol)
  143. # ... DEFINE_SYMBOL is apparently not respected for object libraries?
  144. set_property(TARGET "${target_name}" APPEND PROPERTY COMPILE_DEFINITIONS "${symbol}")
  145. endif()
  146. if(BUILD_METALIBS)
  147. # Apparently neither is CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE?
  148. set_property(TARGET "${target_name}" PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}")
  149. # If we're building dynamic libraries, the object library needs to be -fPIC
  150. if(BUILD_SHARED_LIBS)
  151. set_property(TARGET "${target_name}" PROPERTY POSITION_INDEPENDENT_CODE ON)
  152. endif()
  153. endif()
  154. endfunction(add_component_library)
  155. #
  156. # Function: add_metalib(target [source1 source2] [INIT initfunc [initheader.h]]
  157. # [COMPONENTS component1 ...])
  158. #
  159. # This is add_library, but for metalibs.
  160. #
  161. # The INIT keyword can specify an initialization function/header (which will be
  162. # autogenerated by this function) that calls the underlying component libs'
  163. # init functions.
  164. #
  165. function(add_metalib target_name)
  166. set(components_keyword OFF)
  167. set(init_keyword 0)
  168. set(init_func)
  169. set(init_header "${target_name}.h")
  170. set(components)
  171. set(sources)
  172. foreach(arg ${ARGN})
  173. if(arg STREQUAL "COMPONENTS")
  174. set(components_keyword ON)
  175. set(init_keyword 0)
  176. elseif(arg STREQUAL "INIT")
  177. set(init_keyword 2)
  178. set(components_keyword OFF)
  179. elseif(components_keyword)
  180. list(APPEND components "${arg}")
  181. elseif(init_keyword EQUAL 2)
  182. set(init_func "${arg}")
  183. set(init_keyword 1)
  184. elseif(init_keyword EQUAL 1)
  185. set(init_header "${arg}")
  186. set(init_keyword 0)
  187. else()
  188. list(APPEND sources "${arg}")
  189. endif()
  190. endforeach()
  191. string(REPLACE ";" "|" piped_components "${components}")
  192. set(component_genex_regex ".*TARGET_PROPERTY:(${piped_components}),.*")
  193. set(private_defines)
  194. set(interface_defines)
  195. set(includes)
  196. set(libs)
  197. set(component_init_funcs "")
  198. set(component_init_headers "")
  199. foreach(component ${components})
  200. if(NOT TARGET "${component}")
  201. message(FATAL_ERROR
  202. "Missing component library ${component} referenced by metalib ${target_name}!
  203. (Component library targets must be created BEFORE add_metalib.)")
  204. endif()
  205. get_target_property(is_component "${component}" IS_COMPONENT)
  206. if(NOT is_component)
  207. message(FATAL_ERROR
  208. "Attempted to metalink non-component ${component} into ${target_name}!")
  209. endif()
  210. get_target_property(component_init_header "${component}" INIT_HEADER)
  211. get_target_property(component_init_func "${component}" INIT_FUNCTION)
  212. if(component_init_header)
  213. set(component_init_headers
  214. "${component_init_headers}#include \"${component_init_header}\"\n")
  215. endif()
  216. if(component_init_func)
  217. set(component_init_funcs
  218. "${component_init_funcs} ${component_init_func}();\n")
  219. endif()
  220. if(BUILD_METALIBS)
  221. # This will be an object library we're pulling in; rather than link,
  222. # let's try to "flatten" all of its properties into ours.
  223. # Private defines: Just reference using a generator expression
  224. list(APPEND private_defines "$<TARGET_PROPERTY:${component},COMPILE_DEFINITIONS>")
  225. # Interface defines: Copy those
  226. get_target_property(component_defines "${component}" INTERFACE_COMPILE_DEFINITIONS)
  227. if(component_defines)
  228. list(APPEND interface_defines ${component_defines})
  229. endif()
  230. # Include directories: Filter out anything that references a component
  231. # library or anything in the project path
  232. get_target_property(component_includes "${component}" INTERFACE_INCLUDE_DIRECTORIES)
  233. foreach(component_include ${component_includes})
  234. if(component_include MATCHES "${component_genex_regex}")
  235. # Ignore component references
  236. elseif(component_include MATCHES "^${PROJECT_SOURCE_DIR}")
  237. # Include path within project; should only be included when building
  238. list(APPEND includes "$<BUILD_INTERFACE:${component_include}>")
  239. else()
  240. # Anything else gets included
  241. list(APPEND includes "${component_include}")
  242. endif()
  243. endforeach(component_include)
  244. # Link libraries: Filter out component libraries; we aren't linking
  245. # against them, we're using their objects
  246. get_target_property(component_libraries "${component}" INTERFACE_LINK_LIBRARIES)
  247. foreach(component_library ${component_libraries})
  248. if(NOT component_library)
  249. # NOTFOUND - guess there are no INTERFACE_LINK_LIBRARIES
  250. elseif(component_library MATCHES "${component_genex_regex}")
  251. # Ignore component references
  252. elseif(component_library MATCHES ".*(${piped_components}).*")
  253. # Component library, ignore
  254. else()
  255. # Anything else gets included
  256. list(APPEND libs "${component_library}")
  257. endif()
  258. endforeach(component_library)
  259. # Consume this component's objects
  260. list(APPEND sources "$<TARGET_OBJECTS:${component}>")
  261. else()
  262. list(APPEND libs "${component}")
  263. endif()
  264. endforeach()
  265. if(init_func)
  266. set(init_source_path "${CMAKE_CURRENT_BINARY_DIR}/init_${target_name}.cxx")
  267. set(init_header_path "${CMAKE_CURRENT_BINARY_DIR}/${init_header}")
  268. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.cxx.in" "${init_source_path}")
  269. list(APPEND sources "${init_source_path}")
  270. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.h.in" "${init_header_path}")
  271. install(FILES "${init_header_path}" DESTINATION include/panda3d)
  272. endif()
  273. add_library("${target_name}" ${sources})
  274. target_compile_definitions("${target_name}"
  275. PRIVATE ${private_defines}
  276. INTERFACE ${interface_defines})
  277. target_link_libraries("${target_name}" ${libs})
  278. target_include_directories("${target_name}"
  279. PUBLIC ${includes}
  280. INTERFACE "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/panda3d>")
  281. endfunction(add_metalib)