cc_rules.cmake 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. # Copyright 2017 Google
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. include(CMakeParseArguments)
  15. # cc_library(
  16. # target
  17. # SOURCES sources...
  18. # DEPENDS libraries...
  19. # [EXCLUDE_FROM_ALL]
  20. # )
  21. #
  22. # Defines a new library target with the given target name, sources, and
  23. # dependencies.
  24. function(cc_library name)
  25. set(flag EXCLUDE_FROM_ALL HEADER_ONLY)
  26. set(multi DEPENDS SOURCES)
  27. cmake_parse_arguments(ccl "${flag}" "" "${multi}" ${ARGN})
  28. if(ccl_HEADER_ONLY)
  29. generate_dummy_source(${name} ccl_SOURCES)
  30. endif()
  31. maybe_remove_objc_sources(sources ${ccl_SOURCES})
  32. add_library(${name} ${sources})
  33. add_objc_flags(${name} ${sources})
  34. target_include_directories(
  35. ${name}
  36. PUBLIC
  37. # Put the binary dir first so that the generated config.h trumps any one
  38. # generated statically by a Cocoapods-based build in the same source tree.
  39. ${FIREBASE_BINARY_DIR}
  40. ${FIREBASE_SOURCE_DIR}
  41. )
  42. target_link_libraries(${name} PUBLIC ${ccl_DEPENDS})
  43. if(ccl_EXCLUDE_FROM_ALL)
  44. set_property(
  45. TARGET ${name}
  46. PROPERTY EXCLUDE_FROM_ALL ON
  47. )
  48. endif()
  49. endfunction()
  50. # cc_select(
  51. # interface_library
  52. # CONDITION1 implementation_library1
  53. # [CONDITION2 implementation_library2 ...]
  54. # [DEFAULT implementation_library_default]
  55. # )
  56. #
  57. # Creates an INTERFACE library named `interface_library`.
  58. #
  59. # For each pair of condition and implementation_library, evaluates the condition
  60. # and if true makes that library an INTERFACE link library of
  61. # `interface_library`.
  62. #
  63. # If supplied, uses the `DEFAULT` implementation if no other condition matches.
  64. #
  65. # If no condition matches, fails the configuration cycle with an error message
  66. # indicating that no suitable implementation was found.
  67. function(cc_select library_name)
  68. add_library(${library_name} INTERFACE)
  69. list(LENGTH ARGN length)
  70. if(length GREATER 0)
  71. math(EXPR length "${length} - 1")
  72. foreach(key RANGE 0 ${length} 2)
  73. math(EXPR value "${key} + 1")
  74. list(GET ARGN ${key} condition)
  75. list(GET ARGN ${value} impl_library)
  76. if((${condition} STREQUAL "DEFAULT") OR (${${condition}}))
  77. message("Using ${library_name} = ${impl_library}")
  78. target_link_libraries(
  79. ${library_name} INTERFACE ${impl_library}
  80. )
  81. return()
  82. endif()
  83. endforeach()
  84. endif()
  85. message(FATAL_ERROR "Could not find implementation for ${library_name}")
  86. endfunction()
  87. # cc_binary(
  88. # target
  89. # SOURCES sources...
  90. # DEPENDS libraries...
  91. # [EXCLUDE_FROM_ALL]
  92. # )
  93. #
  94. # Defines a new executable target with the given target name, sources, and
  95. # dependencies.
  96. function(cc_binary name)
  97. set(flag EXCLUDE_FROM_ALL)
  98. set(multi DEPENDS SOURCES)
  99. cmake_parse_arguments(ccb "${flag}" "" "${multi}" ${ARGN})
  100. maybe_remove_objc_sources(sources ${ccb_SOURCES})
  101. add_executable(${name} ${sources})
  102. add_objc_flags(${name} ${sources})
  103. target_compile_options(${name} PRIVATE ${FIREBASE_CXX_FLAGS})
  104. target_include_directories(${name} PRIVATE ${FIREBASE_SOURCE_DIR})
  105. target_link_libraries(${name} PRIVATE ${ccb_DEPENDS})
  106. if(ccb_EXCLUDE_FROM_ALL)
  107. set_property(
  108. TARGET ${name}
  109. PROPERTY EXCLUDE_FROM_ALL ON
  110. )
  111. endif()
  112. endfunction()
  113. # cc_test(
  114. # target
  115. # SOURCES sources...
  116. # DEPENDS libraries...
  117. # )
  118. #
  119. # Defines a new test executable target with the given target name, sources, and
  120. # dependencies. Implicitly adds DEPENDS on GTest::GTest and GTest::Main.
  121. function(cc_test name)
  122. set(multi DEPENDS SOURCES)
  123. cmake_parse_arguments(cct "" "" "${multi}" ${ARGN})
  124. list(APPEND cct_DEPENDS GTest::GTest GTest::Main)
  125. maybe_remove_objc_sources(sources ${cct_SOURCES})
  126. add_executable(${name} ${sources})
  127. add_objc_flags(${name} ${sources})
  128. add_test(${name} ${name})
  129. target_compile_options(${name} PRIVATE ${FIREBASE_CXX_FLAGS})
  130. target_include_directories(${name} PRIVATE ${FIREBASE_SOURCE_DIR})
  131. target_link_libraries(${name} PRIVATE ${cct_DEPENDS})
  132. endfunction()
  133. # cc_fuzz_test(
  134. # target
  135. # DICTIONARY dict_file
  136. # CORPUS corpus_dir
  137. # SOURCES sources...
  138. # DEPENDS libraries...
  139. # )
  140. #
  141. # Defines a new executable fuzz testing target with the given target name,
  142. # (optional) dictionary file, (optional) corpus directory, sources, and
  143. # dependencies. Implicitly adds DEPENDS on 'Fuzzer', which corresponds to
  144. # libFuzzer if fuzzing runs locally or a provided fuzzing library if fuzzing
  145. # runs on OSS Fuzz. If provided, copies the DICTIONARY file as '${target}.dict'
  146. # and copies the CORPUS directory as '${target}_seed_corpus' after building the
  147. # target. This naming convention is critical for OSS Fuzz build script to
  148. # capture new fuzzing targets.
  149. function(cc_fuzz_test name)
  150. # Finds the fuzzer library that is either provided by OSS Fuzz or libFuzzer
  151. # that is manually built from sources.
  152. find_package(Fuzzer REQUIRED)
  153. # Parse arguments of the cc_fuzz_test macro.
  154. set(single DICTIONARY CORPUS)
  155. set(multi DEPENDS SOURCES)
  156. cmake_parse_arguments(ccf "" "${single}" "${multi}" ${ARGN})
  157. list(APPEND ccf_DEPENDS Fuzzer)
  158. cc_binary(
  159. ${name}
  160. SOURCES ${ccf_SOURCES}
  161. DEPENDS ${ccf_DEPENDS}
  162. )
  163. # Copy the dictionary file and corpus directory, if they are defined.
  164. if(DEFINED ccf_DICTIONARY)
  165. add_custom_command(
  166. TARGET ${name} POST_BUILD
  167. COMMAND ${CMAKE_COMMAND} -E copy
  168. ${ccf_DICTIONARY} ${name}.dict
  169. )
  170. endif()
  171. if(DEFINED ccf_CORPUS)
  172. add_custom_command(
  173. TARGET ${name} POST_BUILD
  174. COMMAND ${CMAKE_COMMAND} -E copy_directory
  175. ${ccf_CORPUS} ${name}_seed_corpus
  176. )
  177. endif()
  178. endfunction()
  179. # maybe_remove_objc_sources(output_var sources...)
  180. #
  181. # Removes Objective-C/C++ sources from the given sources if not on an Apple
  182. # platform. Stores the resulting list in the variable named by `output_var`.
  183. function(maybe_remove_objc_sources output_var)
  184. unset(sources)
  185. foreach(source ${ARGN})
  186. get_filename_component(ext ${source} EXT)
  187. if(NOT APPLE AND ((ext STREQUAL ".m") OR (ext STREQUAL ".mm")))
  188. continue()
  189. endif()
  190. list(APPEND sources ${source})
  191. endforeach()
  192. set(${output_var} ${sources} PARENT_SCOPE)
  193. endfunction()
  194. # add_objc_flags(target sources...)
  195. #
  196. # Adds OBJC_FLAGS to the compile options of the given target if any of the
  197. # sources have filenames that indicate they are Objective-C.
  198. function(add_objc_flags target)
  199. set(_has_objc OFF)
  200. foreach(source ${ARGN})
  201. get_filename_component(ext ${source} EXT)
  202. if((ext STREQUAL ".m") OR (ext STREQUAL ".mm"))
  203. set(_has_objc ON)
  204. endif()
  205. endforeach()
  206. if(_has_objc)
  207. target_compile_options(
  208. ${target}
  209. PRIVATE
  210. ${OBJC_FLAGS}
  211. )
  212. target_link_libraries(
  213. ${target}
  214. PRIVATE
  215. "-framework Foundation"
  216. )
  217. endif()
  218. endfunction()
  219. # add_alias(alias_target actual_target)
  220. #
  221. # Adds a library alias target `alias_target` if it does not already exist,
  222. # aliasing to the given `actual_target` target. This allows library dependencies
  223. # to be specified uniformly in terms of the targets found in various
  224. # find_package modules even if the library is being built internally.
  225. function(add_alias ALIAS_TARGET ACTUAL_TARGET)
  226. if(NOT TARGET ${ALIAS_TARGET})
  227. add_library(${ALIAS_TARGET} ALIAS ${ACTUAL_TARGET})
  228. endif()
  229. endfunction()
  230. # objc_framework(
  231. # target
  232. # HEADERS headers...
  233. # SOURCES sources...
  234. # INCLUDES inlude_directories...
  235. # DEFINES macros...
  236. # DEPENDS libraries...
  237. # [EXCLUDE_FROM_ALL]
  238. # )
  239. #
  240. # Defines a new framework target with the given target name and parameters.
  241. #
  242. # If SOURCES is not included, a dummy file will be generated.
  243. function(objc_framework target)
  244. if(APPLE)
  245. set(flag EXCLUDE_FROM_ALL)
  246. set(single VERSION)
  247. set(multi DEPENDS DEFINES HEADERS INCLUDES SOURCES)
  248. cmake_parse_arguments(of "${flag}" "${single}" "${multi}" ${ARGN})
  249. podspec_prep_headers(${target} ${of_HEADERS})
  250. if (NOT cf_SOURCES)
  251. generate_dummy_source(${target} of_SOURCES)
  252. endif()
  253. add_library(
  254. ${target}
  255. STATIC
  256. ${of_SOURCES}
  257. )
  258. set_property(TARGET ${target} PROPERTY PUBLIC_HEADER ${of_HEADERS})
  259. set_property(TARGET ${target} PROPERTY FRAMEWORK ON)
  260. set_property(TARGET ${target} PROPERTY VERSION ${of_VERSION})
  261. if(of_EXCLUDE_FROM_ALL)
  262. set_property(
  263. TARGET ${name}
  264. PROPERTY EXCLUDE_FROM_ALL ON
  265. )
  266. endif()
  267. target_compile_definitions(
  268. ${target}
  269. PUBLIC
  270. ${of_DEFINES}
  271. )
  272. target_compile_options(
  273. ${target}
  274. INTERFACE
  275. -F${CMAKE_CURRENT_BINARY_DIR}
  276. PRIVATE
  277. ${OBJC_FLAGS}
  278. -fno-autolink
  279. -Wno-unused-parameter
  280. )
  281. target_include_directories(
  282. ${target}
  283. PUBLIC ${PROJECT_BINARY_DIR}/Headers
  284. PRIVATE ${of_INCLUDES}
  285. )
  286. target_link_libraries(
  287. ${target} PUBLIC
  288. ${of_DEPENDS}
  289. )
  290. endif()
  291. endfunction()
  292. # generate_dummy_source(name, sources_list)
  293. #
  294. # Generates a dummy source file containing a single symbol, suitable for use as
  295. # a source file in when defining a header-only library.
  296. #
  297. # Appends the generated source file name to the list named by sources_list.
  298. macro(generate_dummy_source name sources_list)
  299. set(__empty_header_only_file "${CMAKE_CURRENT_BINARY_DIR}/${name}_header_only_empty.c")
  300. if(NOT EXISTS ${__empty_header_only_file})
  301. file(WRITE ${__empty_header_only_file}
  302. "// Generated file that keeps header-only CMake libraries happy.
  303. // single meaningless symbol
  304. void ${name}_header_only_fakesym(void) {}
  305. "
  306. )
  307. endif()
  308. list(APPEND ${sources_list} ${__empty_header_only_file})
  309. endmacro()