BuildMetalib.cmake 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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. # SYSTEM include directories should still be reported as SYSTEM, so
  32. # that warnings from those includes are suppressed
  33. set(sys_include_directories
  34. "$<TARGET_PROPERTY:${library},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>")
  35. target_include_directories("${target}" SYSTEM PUBLIC "${sys_include_directories}")
  36. # And for INTERFACE_COMPILE_DEFINITIONS as well
  37. set(compile_definitions "$<TARGET_PROPERTY:${library},INTERFACE_COMPILE_DEFINITIONS>")
  38. set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}")
  39. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS "${compile_definitions}")
  40. # Build up some generator expressions for determining whether `library`
  41. # is a component library or not.
  42. if(library MATCHES ".*::.*")
  43. # "::" messes up CMake's genex parser; fortunately, a library whose
  44. # name contains that is either an interface library or alias, and
  45. # definitely not a component
  46. set(is_component 0)
  47. set(name_of_component "")
  48. set(name_of_non_component "${library}")
  49. else()
  50. set(is_component "$<TARGET_PROPERTY:${library},IS_COMPONENT>")
  51. # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :(
  52. set(is_object "$<STREQUAL:$<TARGET_PROPERTY:${library},TYPE>,OBJECT_LIBRARY>")
  53. set(is_component "$<BOOL:$<${is_object}:${is_component}>>")
  54. set(name_of_component "$<${is_component}:$<TARGET_NAME:${library}>>")
  55. set(name_of_non_component "$<$<NOT:${is_component}>:$<TARGET_NAME:${library}>>")
  56. endif()
  57. # Libraries are only linked transitively if they aren't components.
  58. set_property(TARGET "${target}" APPEND PROPERTY
  59. INTERFACE_LINK_LIBRARIES "${name_of_non_component}")
  60. else()
  61. # This is a file path to an out-of-tree library - this needs to be
  62. # recorded so that the metalib can link them. (They aren't needed at
  63. # all for the object libraries themselves, so they don't have to work
  64. # transitively.)
  65. set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
  66. endif()
  67. endforeach(library)
  68. endfunction(target_link_libraries)
  69. endif()
  70. #
  71. # Function: add_component_library(target [SYMBOL building_symbol]
  72. # [SOURCES] [[NOINIT]/[INIT func [header]]])
  73. #
  74. # Used very similarly to add_library. You can specify a symbol with SYMBOL,
  75. # which works like CMake's own DEFINE_SYMBOL property: it's defined when
  76. # building the library, but not when building something that links against the
  77. # library.
  78. #
  79. # INIT specifies the init function that should be called from a metalib's init
  80. # function when this is added to a metalib. The header parameter can further
  81. # clarify what header declares this function. By default, this is
  82. # init_libTARGET and config_TARGET.h, respectively, where TARGET is the
  83. # target name (with 'p3' stripped off, if applicable). The NOINIT keyword
  84. # suppresses this default.
  85. #
  86. # Note that this function gets to decide whether the component library is
  87. # OBJECT or SHARED, and whether the library is installed or not. Also, as
  88. # a rule, component libraries may only be linked by other component libraries
  89. # in the same metalib - outside of the metalib, you must link the metalib
  90. # itself.
  91. #
  92. function(add_component_library target_name)
  93. set(sources)
  94. unset(symbol)
  95. if(target_name MATCHES "^p3.*")
  96. string(SUBSTRING "${target_name}" 2 -1 name_without_prefix)
  97. else()
  98. set(name_without_prefix "${target_name}")
  99. endif()
  100. set(init_func "init_lib${name_without_prefix}")
  101. set(init_header "config_${name_without_prefix}.h")
  102. set(symbol_keyword OFF)
  103. set(init_keyword 0)
  104. foreach(source ${ARGN})
  105. if(source STREQUAL "SYMBOL")
  106. set(symbol_keyword ON)
  107. set(init_keyword 0)
  108. elseif(source STREQUAL "INIT")
  109. set(symbol_keyword OFF)
  110. set(init_keyword 2)
  111. elseif(source STREQUAL "NOINIT")
  112. set(init_func)
  113. set(init_header)
  114. elseif(symbol_keyword)
  115. set(symbol_keyword OFF)
  116. set(symbol "${source}")
  117. elseif(init_keyword EQUAL 2)
  118. set(init_func "${source}")
  119. set(init_keyword 1)
  120. elseif(init_keyword EQUAL 1)
  121. set(init_header "${source}")
  122. set(init_keyword 0)
  123. else()
  124. list(APPEND sources "${source}")
  125. endif()
  126. endforeach()
  127. if(BUILD_METALIBS)
  128. # CMake 3.0.2 doesn't like .I/.N/.T files! We let it know that they're only
  129. # headers.
  130. foreach(source ${sources})
  131. if(source MATCHES "\\.[INT]$")
  132. set_source_files_properties(${source} PROPERTIES HEADER_FILE_ONLY ON)
  133. endif()
  134. endforeach(source)
  135. add_library("${target_name}" OBJECT ${sources})
  136. else()
  137. add_library("${target_name}" ${sources})
  138. endif()
  139. set_target_properties("${target_name}" PROPERTIES
  140. IS_COMPONENT ON
  141. INIT_FUNCTION "${init_func}"
  142. INIT_HEADER "${init_header}")
  143. if(symbol)
  144. set_property(TARGET "${target_name}" PROPERTY DEFINE_SYMBOL "${symbol}")
  145. if(BUILD_METALIBS)
  146. # ... DEFINE_SYMBOL is apparently not respected for object libraries?
  147. set_property(TARGET "${target_name}" APPEND PROPERTY COMPILE_DEFINITIONS "${symbol}")
  148. # Make sure other component libraries relying on this one inherit the
  149. # symbol
  150. set_property(TARGET "${target_name}" APPEND PROPERTY
  151. INTERFACE_COMPILE_DEFINITIONS "$<$<BOOL:$<TARGET_PROPERTY:IS_COMPONENT>>:${symbol}>")
  152. endif()
  153. endif()
  154. if(BUILD_METALIBS)
  155. # Apparently neither is CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE?
  156. set_property(TARGET "${target_name}" PROPERTY
  157. INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}")
  158. # If we're building dynamic libraries, the object library needs to be -fPIC
  159. if(BUILD_SHARED_LIBS)
  160. set_property(TARGET "${target_name}" PROPERTY
  161. POSITION_INDEPENDENT_CODE ON)
  162. endif()
  163. endif()
  164. endfunction(add_component_library)
  165. #
  166. # Function: add_metalib(target [source1 source2]
  167. # [INCLUDE header1.h ...]
  168. # [INIT initfunc [initheader.h] [EXPORT type name expr]]
  169. # [COMPONENTS component1 ...])
  170. #
  171. # This is add_library, but for metalibs.
  172. #
  173. # The INIT keyword can specify an initialization function/header (which will be
  174. # autogenerated by this function) that calls the underlying component libs'
  175. # init functions.
  176. #
  177. # The EXPORT keyword exports the expression `expr`, which yields a value of
  178. # type `type`, as the undecorated (extern "C") symbol `name`
  179. #
  180. # The INCLUDE keyword allows the init file to pull in additional headers, which
  181. # may be useful for EXPORT.
  182. #
  183. function(add_metalib target_name)
  184. set(components_keyword OFF)
  185. set(init_keyword 0)
  186. set(init_func)
  187. set(init_header "${target_name}.h")
  188. set(export_keyword 0)
  189. set(export_declarations)
  190. set(export_definitions)
  191. set(component_init_headers)
  192. set(components)
  193. set(sources)
  194. foreach(arg ${ARGN})
  195. if(arg STREQUAL "COMPONENTS")
  196. set(components_keyword ON)
  197. set(include_keyword OFF)
  198. set(init_keyword 0)
  199. set(export_keyword 0)
  200. elseif(arg STREQUAL "INCLUDE")
  201. set(include_keyword ON)
  202. set(components_keyword OFF)
  203. set(init_keyword 0)
  204. set(export_keyword 0)
  205. elseif(arg STREQUAL "INIT")
  206. set(init_keyword 2)
  207. set(components_keyword OFF)
  208. set(include_keyword OFF)
  209. set(export_keyword 0)
  210. elseif(arg STREQUAL "EXPORT")
  211. if(NOT init_func)
  212. message(FATAL_ERROR "EXPORT cannot be used before INIT")
  213. endif()
  214. set(export_keyword 3)
  215. set(components_keyword OFF)
  216. set(include_keyword OFF)
  217. set(init_keyword 0)
  218. elseif(components_keyword)
  219. list(APPEND components "${arg}")
  220. elseif(include_keyword)
  221. set(component_init_headers
  222. "${component_init_headers}#include \"${arg}\"\n")
  223. elseif(init_keyword EQUAL 2)
  224. set(init_func "${arg}")
  225. set(init_keyword 1)
  226. elseif(init_keyword EQUAL 1)
  227. set(init_header "${arg}")
  228. set(init_keyword 0)
  229. elseif(export_keyword EQUAL 3)
  230. set(_export_type "${arg}")
  231. set(export_keyword 2)
  232. elseif(export_keyword EQUAL 2)
  233. set(_export_name "${arg}")
  234. set(export_keyword 1)
  235. elseif(export_keyword EQUAL 1)
  236. set(export_declarations
  237. "${export_declarations}\nextern \"C\" IMPORT_CLASS ${_export_type} ${_export_name}();")
  238. set(export_definitions
  239. "${export_definitions}\nextern \"C\" EXPORT_CLASS ${_export_type} ${_export_name}() { return ${arg}; }")
  240. unset(_export_type)
  241. unset(_export_name)
  242. set(export_keyword 0)
  243. else()
  244. list(APPEND sources "${arg}")
  245. endif()
  246. endforeach()
  247. string(REPLACE ";" "|" piped_components "${components}")
  248. set(component_genex_regex ".*TARGET_PROPERTY:(${piped_components}),.*")
  249. set(private_defines)
  250. set(interface_defines)
  251. set(includes)
  252. set(libs)
  253. set(component_init_funcs "")
  254. foreach(component ${components})
  255. if(NOT TARGET "${component}")
  256. message(FATAL_ERROR
  257. "Missing component library ${component} referenced by metalib ${target_name}!
  258. (Component library targets must be created BEFORE add_metalib.)")
  259. endif()
  260. get_target_property(is_component "${component}" IS_COMPONENT)
  261. if(NOT is_component)
  262. message(FATAL_ERROR
  263. "Attempted to metalink non-component ${component} into ${target_name}!")
  264. endif()
  265. get_target_property(component_init_header "${component}" INIT_HEADER)
  266. get_target_property(component_init_func "${component}" INIT_FUNCTION)
  267. if(component_init_header)
  268. set(component_init_headers
  269. "${component_init_headers}#include \"${component_init_header}\"\n")
  270. endif()
  271. if(component_init_func)
  272. set(component_init_funcs
  273. "${component_init_funcs} ${component_init_func}();\n")
  274. endif()
  275. if(BUILD_METALIBS)
  276. # This will be an object library we're pulling in; rather than link,
  277. # let's try to "flatten" all of its properties into ours.
  278. # Private defines: Just reference using a generator expression
  279. list(APPEND private_defines "$<TARGET_PROPERTY:${component},COMPILE_DEFINITIONS>")
  280. # Interface defines: Copy those, but filter out generator expressions
  281. # referencing a component library
  282. get_target_property(component_defines "${component}" INTERFACE_COMPILE_DEFINITIONS)
  283. if(component_defines)
  284. foreach(component_define ${component_defines})
  285. if(component_define MATCHES "${component_genex_regex}")
  286. # Filter, it's a genex referencing one of our components
  287. elseif(component_define MATCHES ".*IS_COMPONENT.*")
  288. # Filter, it's testing to see if the consumer is a component
  289. else()
  290. list(APPEND interface_defines ${component_define})
  291. endif()
  292. endforeach(component_define)
  293. endif()
  294. # Include directories: Filter out anything that references a component
  295. # library or anything in the project path
  296. get_target_property(component_includes "${component}" INTERFACE_INCLUDE_DIRECTORIES)
  297. foreach(component_include ${component_includes})
  298. if(component_include MATCHES "${component_genex_regex}")
  299. # Ignore component references
  300. elseif(component_include MATCHES "^${PROJECT_SOURCE_DIR}")
  301. # Include path within project; should only be included when building
  302. list(APPEND includes "$<BUILD_INTERFACE:${component_include}>")
  303. else()
  304. # Anything else gets included
  305. list(APPEND includes "${component_include}")
  306. endif()
  307. endforeach(component_include)
  308. # Link libraries: Filter out component libraries; we aren't linking
  309. # against them, we're using their objects
  310. get_target_property(component_libraries "${component}" INTERFACE_LINK_LIBRARIES)
  311. foreach(component_library ${component_libraries})
  312. if(NOT component_library)
  313. # NOTFOUND - guess there are no INTERFACE_LINK_LIBRARIES
  314. elseif(component_library MATCHES "${component_genex_regex}")
  315. # Ignore component references
  316. elseif(component_library MATCHES ".*(${piped_components}).*")
  317. # Component library, ignore
  318. else()
  319. # Anything else gets included
  320. list(APPEND libs "${component_library}")
  321. endif()
  322. endforeach(component_library)
  323. # Consume this component's objects
  324. list(APPEND sources "$<TARGET_OBJECTS:${component}>")
  325. else() # NOT BUILD_METALIBS
  326. list(APPEND libs "${component}")
  327. endif()
  328. endforeach()
  329. if(init_func)
  330. set(init_source_path "${CMAKE_CURRENT_BINARY_DIR}/init_${target_name}.cxx")
  331. set(init_header_path "${CMAKE_CURRENT_BINARY_DIR}/${init_header}")
  332. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.cxx.in"
  333. "${init_source_path}")
  334. list(APPEND sources "${init_source_path}")
  335. configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/metalib_init.h.in"
  336. "${init_header_path}")
  337. install(FILES "${init_header_path}" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d)
  338. endif()
  339. add_library("${target_name}" ${sources})
  340. target_compile_definitions("${target_name}"
  341. PRIVATE ${private_defines}
  342. INTERFACE ${interface_defines})
  343. target_link_libraries("${target_name}" ${libs})
  344. target_include_directories("${target_name}"
  345. PUBLIC ${includes}
  346. INTERFACE "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/panda3d>")
  347. endfunction(add_metalib)