cc_rules.cmake 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. # Copyright 2017 Google LLC
  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. # firebase_ios_add_executable(
  17. # target
  18. # sources...
  19. # )
  20. #
  21. # Wraps a call to add_executable (including arguments like EXCLUDE_FROM_ALL)
  22. # that additionally sets common options that apply to all executables in this
  23. # repo.
  24. function(firebase_ios_add_executable target)
  25. firebase_ios_split_target_options(flag ${ARGN})
  26. add_executable(${target} ${flag_REST})
  27. firebase_ios_set_common_target_options(${target} ${flag_OPTIONS})
  28. endfunction()
  29. # firebase_ios_add_library(
  30. # target
  31. # sources...
  32. # )
  33. #
  34. # Wraps a call to add_library (including any arguments like EXCLUDE_FROM_ALL)
  35. # that additionally sets common options that apply to all libraries in this
  36. # repo.
  37. function(firebase_ios_add_library target)
  38. firebase_ios_split_target_options(flag ${ARGN})
  39. add_library(${target} ${flag_REST})
  40. firebase_ios_set_common_target_options(${target} ${flag_OPTIONS})
  41. endfunction()
  42. # firebase_ios_add_framework(
  43. # target
  44. # sources...
  45. # )
  46. #
  47. # Wraps a call to add_library (including arguments like EXCLUDE_FROM_ALL) that
  48. # additionally sets common options that apply to all frameworks in this repo.
  49. #
  50. # Note that this does not build an actual framework bundle--even recent versions
  51. # of CMake (3.15.5 as of this writing) produce problematic output when
  52. # frameworks are enabled. However `firebase_ios_add_framework` along with
  53. # `firebase_ios_framework_public_headers` produces a library target that
  54. # can be compiled against as if it *were* a framework. Notably, imports of the
  55. # form #import <Framework/Framework.h> and public headers that refer to each
  56. # other unqualified will work.
  57. function(firebase_ios_add_framework target)
  58. firebase_ios_split_target_options(flag ${ARGN})
  59. add_library(${target} ${flag_REST})
  60. firebase_ios_set_common_target_options(${target} ${flag_OPTIONS})
  61. target_link_libraries(${target} INTERFACE -ObjC)
  62. endfunction()
  63. # firebase_ios_framework_public_headers(
  64. # target
  65. # headers...
  66. # )
  67. #
  68. # Prepares the given headers for use as public headers of the given target.
  69. # This emulates CocoaPods behavior of flattening public headers by creating
  70. # a separate directory tree in the build output, consisting of symbolic links to
  71. # the actual header files. This makes it possible to use imports of these forms:
  72. #
  73. # * #import <Framework/Header.h>
  74. # * #import "Header.h"
  75. function(firebase_ios_framework_public_headers target)
  76. target_sources(${target} PRIVATE ${ARGN})
  77. podspec_prep_headers(${target} ${ARGN})
  78. target_include_directories(
  79. ${target}
  80. # ${target} is a subdirectory, making <Framework/Header.h> work.
  81. PUBLIC ${PROJECT_BINARY_DIR}/Headers
  82. # Make unqualified imports work too, often needed for peer imports from
  83. # one public header to another.
  84. PUBLIC ${PROJECT_BINARY_DIR}/Headers/${target}
  85. )
  86. endfunction()
  87. # firebase_ios_add_test(
  88. # target
  89. # sources...
  90. # )
  91. #
  92. # Wraps a call to `add_executable` (including arguments like `EXCLUDE_FROM_ALL`)
  93. # and `add_test` that additionally sets common options that apply to all tests
  94. # in this repo.
  95. #
  96. # Also adds dependencies on GTest::GTest and GTest::Main.
  97. function(firebase_ios_add_test target)
  98. firebase_ios_split_target_options(flag ${ARGN})
  99. add_executable(${target} ${flag_REST})
  100. add_test(${target} ${target})
  101. firebase_ios_set_common_target_options(${target} ${flag_OPTIONS})
  102. # Set a preprocessor define so that tests can distinguish between having been
  103. # built by Xcode vs. cmake.
  104. target_compile_definitions(
  105. ${target}
  106. PRIVATE
  107. FIREBASE_TESTS_BUILT_BY_CMAKE
  108. )
  109. target_link_libraries(${target} PRIVATE GTest::GTest GTest::Main)
  110. endfunction()
  111. # firebase_ios_add_objc_test(
  112. # target
  113. # host_app
  114. # sources...
  115. # )
  116. #
  117. # Creates an XCTest bundle suitable for running tests written in Objective-C by
  118. # wrapping a call to `xctest_add_bundle` (including arguments like
  119. # `EXCLUDE_FROM_ALL`) and `xctest_add_test` that additionally sets common
  120. # options that apply to all tests in this repo.
  121. function(firebase_ios_add_objc_test target host)
  122. xctest_add_bundle(${target} ${host} ${ARGN})
  123. firebase_ios_set_common_target_options(${target})
  124. xctest_add_test(${target} ${target})
  125. if(WITH_ASAN)
  126. set_property(
  127. TEST ${target} APPEND PROPERTY
  128. ENVIRONMENT DYLD_INSERT_LIBRARIES=${CLANG_ASAN_DYLIB}
  129. )
  130. endif()
  131. if(WITH_TSAN)
  132. set_property(
  133. TEST ${target} APPEND PROPERTY
  134. ENVIRONMENT DYLD_INSERT_LIBRARIES=${CLANG_TSAN_DYLIB}
  135. )
  136. endif()
  137. endfunction()
  138. # firebase_ios_add_fuzz_test(
  139. # target
  140. # DICTIONARY dict_file
  141. # CORPUS corpus_dir
  142. # sources...
  143. # )
  144. #
  145. # Defines a new executable fuzz testing target with the given target name,
  146. # (optional) dictionary file, (optional) corpus directory, along with the given
  147. # sources and arguments you might pass to add_executable.
  148. #
  149. # Implicitly adds a dependency on the `Fuzzer` library, which corresponds to
  150. # libFuzzer if fuzzing runs locally or a provided fuzzing library if fuzzing
  151. # runs on OSS Fuzz.
  152. #
  153. # If provided, copies the DICTIONARY file as '${target}.dict' and copies the
  154. # CORPUS directory as '${target}_seed_corpus' after building the target. This
  155. # naming convention is critical for OSS Fuzz build script to capture new fuzzing
  156. # targets.
  157. function(firebase_ios_add_fuzz_test target)
  158. firebase_ios_split_target_options(flag ${ARGN})
  159. set(single DICTIONARY CORPUS)
  160. cmake_parse_arguments(args "" "${single}" "" ${flag_REST})
  161. firebase_ios_add_executable(${target} ${args_UNPARSED_ARGUMENTS})
  162. firebase_ios_set_common_target_options(${target} ${flag_OPTIONS})
  163. target_link_libraries(${target} PRIVATE Fuzzer)
  164. # Copy the dictionary file and corpus directory, if they are defined.
  165. if(DEFINED ccf_DICTIONARY)
  166. add_custom_command(
  167. TARGET ${target} POST_BUILD
  168. COMMAND ${CMAKE_COMMAND} -E copy
  169. ${ccf_DICTIONARY} ${target}.dict
  170. )
  171. endif()
  172. if(DEFINED ccf_CORPUS)
  173. add_custom_command(
  174. TARGET ${target} POST_BUILD
  175. COMMAND ${CMAKE_COMMAND} -E copy_directory
  176. ${ccf_CORPUS} ${target}_seed_corpus
  177. )
  178. endif()
  179. endfunction()
  180. # firebase_ios_add_alias(alias_target actual_target)
  181. #
  182. # Adds a library alias target `alias_target` if it does not already exist,
  183. # aliasing to the given `actual_target` target. This allows library dependencies
  184. # to be specified uniformly in terms of the targets found in various
  185. # find_package modules even if the library is being built internally.
  186. function(firebase_ios_add_alias ALIAS_TARGET ACTUAL_TARGET)
  187. if(NOT TARGET ${ALIAS_TARGET})
  188. add_library(${ALIAS_TARGET} ALIAS ${ACTUAL_TARGET})
  189. endif()
  190. endfunction()
  191. # firebase_ios_generate_dummy_source(target, sources_list)
  192. #
  193. # Generates a dummy source file containing a single symbol, suitable for use as
  194. # a source file in when defining a header-only library.
  195. #
  196. # Appends the generated source file target to the list named by sources_list.
  197. macro(firebase_ios_generate_dummy_source target sources_list)
  198. set(
  199. __empty_header_only_file
  200. "${CMAKE_CURRENT_BINARY_DIR}/${target}_header_only_empty.cc"
  201. )
  202. if(NOT EXISTS ${__empty_header_only_file})
  203. file(WRITE ${__empty_header_only_file}
  204. "// Generated file that keeps header-only CMake libraries happy.
  205. // single meaningless symbol
  206. void ${target}_header_only_fakesym() {}
  207. "
  208. )
  209. endif()
  210. list(APPEND ${sources_list} ${__empty_header_only_file})
  211. endmacro()
  212. # Splits the given arguments into two lists: those suitable for passing to
  213. # `firebase_ios_set_common_target_options` and everything else, usually passed
  214. # to `add_library`, `add_executable`, or similar.
  215. #
  216. # The resulting lists are set in the parent scope as `${prefix}_OPTIONS` for use
  217. # when calling `firebase_ios_set_common_target_options`, and `${prefix}_REST`
  218. # that contains any arguments that were unrecognized.
  219. #
  220. # See `firebase_ios_set_common_target_options` for details on which arguments
  221. # are split out.
  222. function(firebase_ios_split_target_options prefix)
  223. set(option_names DISABLE_STRICT_WARNINGS)
  224. set(${prefix}_OPTIONS)
  225. set(${prefix}_REST)
  226. foreach(arg ${ARGN})
  227. list(FIND option_names ${arg} option_index)
  228. if(NOT option_index EQUAL -1)
  229. list(APPEND ${prefix}_OPTIONS ${arg})
  230. else()
  231. list(APPEND ${prefix}_REST ${arg})
  232. endif()
  233. endforeach()
  234. set(${prefix}_OPTIONS ${${prefix}_OPTIONS} PARENT_SCOPE)
  235. set(${prefix}_REST ${${prefix}_REST} PARENT_SCOPE)
  236. endfunction()
  237. # Sets common options for a given target. This includes:
  238. #
  239. # * `DISABLE_STRICT_WARNINGS`: disables stricter warnings usually applied to
  240. # targets in this repo. Use this to compile code that's local but doesn't
  241. # hold itself to Firestore's warning strictness.
  242. #
  243. # All other arguments are ignored.
  244. function(firebase_ios_set_common_target_options target)
  245. set(options DISABLE_STRICT_WARNINGS)
  246. cmake_parse_arguments(flag "${options}" "" "" ${ARGN})
  247. if(flag_DISABLE_STRICT_WARNINGS)
  248. set(cxx_flags ${FIREBASE_IOS_CXX_FLAGS})
  249. set(objc_flags ${FIREBASE_IOS_OBJC_FLAGS})
  250. else()
  251. set(cxx_flags ${FIREBASE_IOS_CXX_FLAGS_STRICT})
  252. set(objc_flags ${FIREBASE_IOS_OBJC_FLAGS_STRICT})
  253. endif()
  254. target_compile_options(${target} PRIVATE ${cxx_flags})
  255. if(APPLE)
  256. target_compile_options(${target} PRIVATE ${objc_flags})
  257. endif()
  258. target_include_directories(
  259. ${target} PRIVATE
  260. # Put the binary dir first so that the generated config.h trumps any one
  261. # generated statically by a Cocoapods-based build in the same source tree.
  262. ${PROJECT_BINARY_DIR}
  263. ${PROJECT_SOURCE_DIR}
  264. )
  265. endfunction()
  266. # firebase_ios_glob(
  267. # list_variable
  268. # [APPEND]
  269. # [globs...]
  270. # [EXCLUDE globs...]
  271. # [INCLUDE globs...]
  272. # )
  273. #
  274. # Applies globbing rules, accumulating a list of matching files, and sets the
  275. # result to list_variable in the parent scope.
  276. #
  277. # If APPEND is specified, the result will start with any existing values in the
  278. # list_variable. Otherwise, the list_variable will be overwritten with only the
  279. # matches of this invocation.
  280. #
  281. # EXCLUDE and INCLUDE change the direction of the match. EXCLUDE will subtract
  282. # files matching the glob patterns from the result. INCLUDE will again add
  283. # files matching the glob patterns to the result. These can be used sequentially
  284. # to finely tune the match.
  285. #
  286. # As a special case, if invoked with EXCLUDE in the first argument position,
  287. # this is treated as if APPEND had been passed. This makes the common case of
  288. # subtracting something from the list less prone to error. That is, these two
  289. # invocations are equivalent:
  290. #
  291. # firebase_ios_glob(sources APPEND EXCLUDE *.h)
  292. # firebase_ios_glob(sources EXCLUDE *.h)
  293. #
  294. # Without this exception, the second form would always end up an empty list and
  295. # would never be useful.
  296. function(firebase_ios_glob list_variable)
  297. set(result)
  298. set(list_op APPEND)
  299. set(first_arg TRUE)
  300. foreach(arg IN LISTS ARGN)
  301. if(arg STREQUAL "APPEND")
  302. list(APPEND result ${${list_variable}})
  303. elseif(arg STREQUAL "INCLUDE")
  304. set(list_op APPEND)
  305. elseif(arg STREQUAL "EXCLUDE")
  306. if(first_arg)
  307. list(APPEND result ${${list_variable}})
  308. endif()
  309. set(list_op REMOVE_ITEM)
  310. else()
  311. # Anything else is a glob pattern. Match the pattern and perform the list
  312. # operation implied by the current INCLUDE or EXCLUDE mode.
  313. file(GLOB matched ${arg})
  314. list(${list_op} result ${matched})
  315. endif()
  316. set(first_arg FALSE)
  317. endforeach()
  318. set(${list_variable} ${result} PARENT_SCOPE)
  319. endfunction()