cc_rules.cmake 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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. include(FindASANDylib)
  16. # cc_library(
  17. # target
  18. # SOURCES sources...
  19. # DEPENDS libraries...
  20. # [EXCLUDE_FROM_ALL]
  21. # )
  22. #
  23. # Defines a new library target with the given target name, sources, and
  24. # dependencies.
  25. function(cc_library name)
  26. set(flag EXCLUDE_FROM_ALL HEADER_ONLY)
  27. set(multi DEPENDS SOURCES)
  28. cmake_parse_arguments(ccl "${flag}" "" "${multi}" ${ARGN})
  29. if(ccl_HEADER_ONLY)
  30. generate_dummy_source(${name} ccl_SOURCES)
  31. endif()
  32. maybe_remove_objc_sources(sources ${ccl_SOURCES})
  33. add_library(${name} ${sources})
  34. add_objc_flags(${name} ${sources})
  35. target_include_directories(
  36. ${name}
  37. PUBLIC
  38. # Put the binary dir first so that the generated config.h trumps any one
  39. # generated statically by a Cocoapods-based build in the same source tree.
  40. ${FIREBASE_BINARY_DIR}
  41. ${FIREBASE_SOURCE_DIR}
  42. )
  43. target_link_libraries(${name} PUBLIC ${ccl_DEPENDS})
  44. if(ccl_EXCLUDE_FROM_ALL)
  45. set_property(
  46. TARGET ${name}
  47. PROPERTY EXCLUDE_FROM_ALL ON
  48. )
  49. endif()
  50. endfunction()
  51. # cc_select(
  52. # interface_library
  53. # CONDITION1 implementation_library1
  54. # [CONDITION2 implementation_library2 ...]
  55. # [DEFAULT implementation_library_default]
  56. # )
  57. #
  58. # Creates an INTERFACE library named `interface_library`.
  59. #
  60. # For each pair of condition and implementation_library, evaluates the condition
  61. # and if true makes that library an INTERFACE link library of
  62. # `interface_library`.
  63. #
  64. # If supplied, uses the `DEFAULT` implementation if no other condition matches.
  65. #
  66. # If no condition matches, fails the configuration cycle with an error message
  67. # indicating that no suitable implementation was found.
  68. function(cc_select library_name)
  69. add_library(${library_name} INTERFACE)
  70. list(LENGTH ARGN length)
  71. if(length GREATER 0)
  72. math(EXPR length "${length} - 1")
  73. foreach(key RANGE 0 ${length} 2)
  74. math(EXPR value "${key} + 1")
  75. list(GET ARGN ${key} condition)
  76. list(GET ARGN ${value} impl_library)
  77. if((${condition} STREQUAL "DEFAULT") OR (${${condition}}))
  78. message("Using ${library_name} = ${impl_library}")
  79. target_link_libraries(
  80. ${library_name} INTERFACE ${impl_library}
  81. )
  82. return()
  83. endif()
  84. endforeach()
  85. endif()
  86. message(FATAL_ERROR "Could not find implementation for ${library_name}")
  87. endfunction()
  88. # cc_binary(
  89. # target
  90. # SOURCES sources...
  91. # DEPENDS libraries...
  92. # [EXCLUDE_FROM_ALL]
  93. # )
  94. #
  95. # Defines a new executable target with the given target name, sources, and
  96. # dependencies.
  97. function(cc_binary name)
  98. set(flag EXCLUDE_FROM_ALL)
  99. set(multi DEPENDS SOURCES)
  100. cmake_parse_arguments(ccb "${flag}" "" "${multi}" ${ARGN})
  101. maybe_remove_objc_sources(sources ${ccb_SOURCES})
  102. add_executable(${name} ${sources})
  103. add_objc_flags(${name} ${sources})
  104. target_compile_options(${name} PRIVATE ${FIREBASE_CXX_FLAGS})
  105. target_include_directories(${name} PRIVATE ${FIREBASE_SOURCE_DIR})
  106. target_link_libraries(${name} PRIVATE ${ccb_DEPENDS})
  107. if(ccb_EXCLUDE_FROM_ALL)
  108. set_property(
  109. TARGET ${name}
  110. PROPERTY EXCLUDE_FROM_ALL ON
  111. )
  112. endif()
  113. endfunction()
  114. # cc_test(
  115. # target
  116. # SOURCES sources...
  117. # DEPENDS libraries...
  118. # )
  119. #
  120. # Defines a new test executable target with the given target name, sources, and
  121. # dependencies. Implicitly adds DEPENDS on GTest::GTest and GTest::Main.
  122. function(cc_test name)
  123. set(multi DEPENDS SOURCES)
  124. cmake_parse_arguments(cct "" "" "${multi}" ${ARGN})
  125. list(APPEND cct_DEPENDS GTest::GTest GTest::Main)
  126. maybe_remove_objc_sources(sources ${cct_SOURCES})
  127. add_executable(${name} ${sources})
  128. add_objc_flags(${name} ${sources})
  129. add_test(${name} ${name})
  130. target_compile_options(${name} PRIVATE ${FIREBASE_CXX_FLAGS})
  131. target_include_directories(${name} PRIVATE ${FIREBASE_SOURCE_DIR})
  132. target_link_libraries(${name} PRIVATE ${cct_DEPENDS})
  133. endfunction()
  134. # cc_fuzz_test(
  135. # target
  136. # DICTIONARY dict_file
  137. # CORPUS corpus_dir
  138. # SOURCES sources...
  139. # DEPENDS libraries...
  140. # )
  141. #
  142. # Defines a new executable fuzz testing target with the given target name,
  143. # (optional) dictionary file, (optional) corpus directory, sources, and
  144. # dependencies. Implicitly adds DEPENDS on 'Fuzzer', which corresponds to
  145. # libFuzzer if fuzzing runs locally or a provided fuzzing library if fuzzing
  146. # runs on OSS Fuzz. If provided, copies the DICTIONARY file as '${target}.dict'
  147. # and copies the CORPUS directory as '${target}_seed_corpus' after building the
  148. # target. This naming convention is critical for OSS Fuzz build script to
  149. # capture new fuzzing targets.
  150. function(cc_fuzz_test name)
  151. # Finds the fuzzer library that is either provided by OSS Fuzz or libFuzzer
  152. # that is manually built from sources.
  153. find_package(Fuzzer REQUIRED)
  154. # Parse arguments of the cc_fuzz_test macro.
  155. set(single DICTIONARY CORPUS)
  156. set(multi DEPENDS SOURCES)
  157. cmake_parse_arguments(ccf "" "${single}" "${multi}" ${ARGN})
  158. list(APPEND ccf_DEPENDS Fuzzer)
  159. cc_binary(
  160. ${name}
  161. SOURCES ${ccf_SOURCES}
  162. DEPENDS ${ccf_DEPENDS}
  163. )
  164. # Copy the dictionary file and corpus directory, if they are defined.
  165. if(DEFINED ccf_DICTIONARY)
  166. add_custom_command(
  167. TARGET ${name} POST_BUILD
  168. COMMAND ${CMAKE_COMMAND} -E copy
  169. ${ccf_DICTIONARY} ${name}.dict
  170. )
  171. endif()
  172. if(DEFINED ccf_CORPUS)
  173. add_custom_command(
  174. TARGET ${name} POST_BUILD
  175. COMMAND ${CMAKE_COMMAND} -E copy_directory
  176. ${ccf_CORPUS} ${name}_seed_corpus
  177. )
  178. endif()
  179. endfunction()
  180. # maybe_remove_objc_sources(output_var sources...)
  181. #
  182. # Removes Objective-C/C++ sources from the given sources if not on an Apple
  183. # platform. Stores the resulting list in the variable named by `output_var`.
  184. function(maybe_remove_objc_sources output_var)
  185. unset(sources)
  186. foreach(source ${ARGN})
  187. get_filename_component(ext ${source} EXT)
  188. if(NOT APPLE AND ((ext STREQUAL ".m") OR (ext STREQUAL ".mm")))
  189. continue()
  190. endif()
  191. list(APPEND sources ${source})
  192. endforeach()
  193. set(${output_var} ${sources} PARENT_SCOPE)
  194. endfunction()
  195. # add_objc_flags(target sources...)
  196. #
  197. # Adds OBJC_FLAGS to the compile options of the given target if any of the
  198. # sources have filenames that indicate they are Objective-C.
  199. function(add_objc_flags target)
  200. set(_has_objc OFF)
  201. foreach(source ${ARGN})
  202. get_filename_component(ext ${source} EXT)
  203. if((ext STREQUAL ".m") OR (ext STREQUAL ".mm"))
  204. set(_has_objc ON)
  205. endif()
  206. endforeach()
  207. if(_has_objc)
  208. target_compile_options(
  209. ${target}
  210. PRIVATE
  211. ${OBJC_FLAGS}
  212. )
  213. target_link_libraries(
  214. ${target}
  215. PRIVATE
  216. "-framework Foundation"
  217. )
  218. endif()
  219. endfunction()
  220. # add_alias(alias_target actual_target)
  221. #
  222. # Adds a library alias target `alias_target` if it does not already exist,
  223. # aliasing to the given `actual_target` target. This allows library dependencies
  224. # to be specified uniformly in terms of the targets found in various
  225. # find_package modules even if the library is being built internally.
  226. function(add_alias ALIAS_TARGET ACTUAL_TARGET)
  227. if(NOT TARGET ${ALIAS_TARGET})
  228. add_library(${ALIAS_TARGET} ALIAS ${ACTUAL_TARGET})
  229. endif()
  230. endfunction()
  231. # objc_framework(
  232. # target
  233. # HEADERS headers...
  234. # SOURCES sources...
  235. # INCLUDES inlude_directories...
  236. # DEFINES macros...
  237. # DEPENDS libraries...
  238. # [EXCLUDE_FROM_ALL]
  239. # )
  240. #
  241. # Defines a new framework target with the given target name and parameters.
  242. #
  243. # If SOURCES is not included, a dummy file will be generated.
  244. function(objc_framework target)
  245. if(APPLE)
  246. set(flag SHARED EXCLUDE_FROM_ALL)
  247. set(single VERSION)
  248. set(multi DEPENDS DEFINES HEADERS INCLUDES SOURCES)
  249. cmake_parse_arguments(of "${flag}" "${single}" "${multi}" ${ARGN})
  250. if (NOT cf_SOURCES)
  251. generate_dummy_source(${target} of_SOURCES)
  252. endif()
  253. if(of_SHARED)
  254. set(of_SHARED_FLAG SHARED)
  255. endif()
  256. add_library(
  257. ${target}
  258. ${of_SHARED_FLAG}
  259. ${of_HEADERS}
  260. ${of_SOURCES}
  261. )
  262. add_objc_flags(${target} ${of_SOURCES})
  263. set_property(TARGET ${target} PROPERTY PUBLIC_HEADER ${of_HEADERS})
  264. set_property(TARGET ${target} PROPERTY FRAMEWORK ON)
  265. set_property(TARGET ${target} PROPERTY VERSION ${of_VERSION})
  266. if(of_EXCLUDE_FROM_ALL)
  267. set_property(TARGET ${name} PROPERTY EXCLUDE_FROM_ALL ON)
  268. endif()
  269. target_compile_definitions(
  270. ${target}
  271. PUBLIC
  272. ${of_DEFINES}
  273. )
  274. target_compile_options(
  275. ${target}
  276. INTERFACE
  277. -F${CMAKE_CURRENT_BINARY_DIR}
  278. PRIVATE
  279. ${OBJC_FLAGS}
  280. -fno-autolink
  281. -Wno-unused-parameter
  282. )
  283. # Include directories are carefully crafted to support the following forms
  284. # of import, both before and after the framework is built.
  285. # * #import <Framework/Header.h>
  286. # * #import "Header.h"
  287. #
  288. # Do not use #import "Firestore/Source/Public/Header.h".
  289. podspec_prep_headers(${target} ${of_HEADERS})
  290. target_include_directories(
  291. ${target}
  292. # Before the framework is built, Framework.framework/Headers isn't
  293. # available yet, so use podspec_prep_headers to create symbolic links
  294. # fitting the <Framework/Header.h> pattern.
  295. PRIVATE ${PROJECT_BINARY_DIR}/Headers
  296. PRIVATE ${of_INCLUDES}
  297. # Building the framework copies public headers into it. Unfortunately
  298. # these copies defeat Clang's #import deduplication mechanism, so the
  299. # podspec_prep_headers versions (and any original locations) must not be
  300. # made available to clients of the framework. Clients get the qualified
  301. # form through the public header support in Clang's module system, and
  302. # unqualified names through this additional entry.
  303. PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/${target}.framework/Headers
  304. )
  305. target_link_options(
  306. ${target} PRIVATE
  307. -ObjC
  308. )
  309. target_link_libraries(
  310. ${target} PUBLIC
  311. ${of_DEPENDS}
  312. )
  313. endif()
  314. endfunction()
  315. function(objc_test target)
  316. if(APPLE)
  317. set(flag EXCLUDE_FROM_ALL)
  318. set(single HOST VERSION WORKING_DIRECTORY)
  319. set(multi DEPENDS DEFINES HEADERS INCLUDES SOURCES)
  320. cmake_parse_arguments(ot "${flag}" "${single}" "${multi}" ${ARGN})
  321. xctest_add_bundle(
  322. ${target}
  323. ${ot_HOST}
  324. ${ot_SOURCES}
  325. )
  326. add_objc_flags(
  327. ${target}
  328. ${ot_SOURCES}
  329. )
  330. target_link_libraries(
  331. ${target}
  332. PRIVATE
  333. ${ot_DEPENDS}
  334. )
  335. xctest_add_test(
  336. ${target}
  337. ${target}
  338. )
  339. if(ot_WORKING_DIRECTORY)
  340. set_property(
  341. TEST ${target} PROPERTY
  342. WORKING_DIRECTORY ${ot_WORKING_DIRECTORY}
  343. )
  344. endif()
  345. if(APPLE AND WITH_ASAN)
  346. set_property(
  347. TEST ${target} APPEND PROPERTY
  348. ENVIRONMENT
  349. DYLD_INSERT_LIBRARIES=${CLANG_ASAN_DYLIB}
  350. )
  351. endif()
  352. if(APPLE AND WITH_TSAN)
  353. set_property(
  354. TEST ${target} APPEND PROPERTY
  355. ENVIRONMENT
  356. DYLD_INSERT_LIBRARIES=${CLANG_TSAN_DYLIB}
  357. )
  358. endif()
  359. endif()
  360. endfunction()
  361. # generate_dummy_source(name, sources_list)
  362. #
  363. # Generates a dummy source file containing a single symbol, suitable for use as
  364. # a source file in when defining a header-only library.
  365. #
  366. # Appends the generated source file name to the list named by sources_list.
  367. macro(generate_dummy_source name sources_list)
  368. set(__empty_header_only_file "${CMAKE_CURRENT_BINARY_DIR}/${name}_header_only_empty.c")
  369. if(NOT EXISTS ${__empty_header_only_file})
  370. file(WRITE ${__empty_header_only_file}
  371. "// Generated file that keeps header-only CMake libraries happy.
  372. // single meaningless symbol
  373. void ${name}_header_only_fakesym(void) {}
  374. "
  375. )
  376. endif()
  377. list(APPEND ${sources_list} ${__empty_header_only_file})
  378. endmacro()