BuildMetalib.cmake 12 KB

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