diff --git a/CMakeLists.txt b/CMakeLists.txt index 831ce32212..c7cac4ae96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,64 +218,8 @@ target_include_directories( enable_testing() include(compiler_setup) - -# Firebase packages - -# Set variables that CocoaPods expects to be able to expand in values it -# includes in the xcconfigs it generates. -set(PODS_TARGET_SRCROOT ${FIREBASE_SOURCE_DIR}) - -podspec_framework( - ${FIREBASE_SOURCE_DIR}/GoogleUtilities.podspec - SPECS Logger -) - -podspec_framework(${FIREBASE_SOURCE_DIR}/GoogleDataTransport.podspec) - -podspec_framework(${FIREBASE_SOURCE_DIR}/GoogleDataTransportCCTSupport.podspec) - -podspec_framework(${FIREBASE_SOURCE_DIR}/FirebaseCore.podspec) - -podspec_framework(${FIREBASE_SOURCE_DIR}/FirebaseCoreDiagnostics.podspec) - -podspec_framework( - ${FIREBASE_SOURCE_DIR}/FirebaseCoreDiagnosticsInterop.podspec -) - -if(APPLE) - # FirebaseCoreDiagnosticsInterop has no source files but CMake can't build - # frameworks that don't have sources. Generate an inconsequential source - # file so that the library can be linked. - file( - WRITE ${CMAKE_CURRENT_BINARY_DIR}/FirebaseCoreDiagnosticsInteropDummy.c - "// generated file for header-only CMake support. - __attribute__((unused)) - static void FirebaseCoreDiagnosticsInteropFakeSymbol() {} - " - ) - target_sources( - FirebaseCoreDiagnosticsInterop - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/FirebaseCoreDiagnosticsInteropDummy.c - ) -endif() - -podspec_framework(${FIREBASE_SOURCE_DIR}/FirebaseAuthInterop.podspec) - -if(APPLE) - # FirebaseAuthInterop has no source files but CMake can't build frameworks - # that don't have sources. Generate an inconsequential source file so that - # the library can be linked. - file( - WRITE ${CMAKE_CURRENT_BINARY_DIR}/FirebaseAuthInteropDummy.c - "// generated file for header-only CMake support. - __attribute__((unused)) - static void FirebaseAuthInteropFakeSymbol() {} - " - ) - target_sources( - FirebaseAuthInterop - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/FirebaseAuthInteropDummy.c - ) -endif() - +add_subdirectory(Firebase/Core) add_subdirectory(Firestore) +add_subdirectory(GoogleUtilities) +add_subdirectory(Interop/Auth) +add_subdirectory(Interop/CoreDiagnostics) diff --git a/Firebase/Core/CMakeLists.txt b/Firebase/Core/CMakeLists.txt new file mode 100644 index 0000000000..9e0d24345a --- /dev/null +++ b/Firebase/Core/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(APPLE) + file(GLOB sources *.m) + file(GLOB headers Private/*.h Public/*.h) + + podspec_version(core_version ${PROJECT_SOURCE_DIR}/FirebaseCore.podspec) + firebase_version(fb_version ${PROJECT_SOURCE_DIR}/FirebaseCore.podspec) + + objc_framework( + FirebaseCore + SOURCES ${sources} + HEADERS ${headers} + VERSION ${core_version} + DEFINES + FIRCore_VERSION=${core_version} + Firebase_VERSION=${fb_version} + INCLUDES + Private + Public + DEPENDS + GoogleUtilities + FirebaseCoreDiagnosticsInterop + "-framework Foundation" + "-framework AppKit" + EXCLUDE_FROM_ALL + ) +endif() diff --git a/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt index 21a27cef4e..e586d86837 100644 --- a/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/auth/CMakeLists.txt @@ -34,11 +34,6 @@ cc_library( DEPENDS FirebaseAuthInterop FirebaseCore - FirebaseCoreDiagnostics - FirebaseCoreDiagnosticsInterop - GoogleDataTransport - GoogleDataTransportCCTSupport - GoogleUtilities firebase_firestore_auth_base EXCLUDE_FROM_ALL ) diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 5f75a8c806..6506a051ed 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -94,11 +94,6 @@ cc_library( string_format.h DEPENDS FirebaseCore - FirebaseCoreDiagnostics - FirebaseCoreDiagnosticsInterop - GoogleDataTransport - GoogleDataTransportCCTSupport - GoogleUtilities absl_base absl_strings EXCLUDE_FROM_ALL diff --git a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt index 6b5b6afc2f..23355650e2 100644 --- a/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/testutil/CMakeLists.txt @@ -20,11 +20,6 @@ cc_library( xcgmock.h DEPENDS FirebaseCore - FirebaseCoreDiagnostics - FirebaseCoreDiagnosticsInterop - GoogleDataTransport - GoogleDataTransportCCTSupport - GoogleUtilities absl_strings EXCLUDE_FROM_ALL ) diff --git a/GoogleUtilities/CMakeLists.txt b/GoogleUtilities/CMakeLists.txt new file mode 100644 index 0000000000..8bc1f4aa6e --- /dev/null +++ b/GoogleUtilities/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(APPLE) + # Environment and Logger subspecs + file( + GLOB sources + Environment/third_party/*.m + Logger/*.m + ) + file( + GLOB headers + Environment/third_party/*.h + Logger/Private/*.h + Logger/Public/*.h + ) + + podspec_version(version ${PROJECT_SOURCE_DIR}/GoogleUtilities.podspec) + + objc_framework( + GoogleUtilities + SOURCES ${sources} + HEADERS ${headers} + VERSION ${version} + INCLUDES + Private + Public + DEPENDS + "-framework Foundation" + EXCLUDE_FROM_ALL + ) +endif() diff --git a/Interop/Auth/CMakeLists.txt b/Interop/Auth/CMakeLists.txt new file mode 100644 index 0000000000..b55ed7b6c0 --- /dev/null +++ b/Interop/Auth/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(APPLE) + file(GLOB headers Public/*.h) + + podspec_version(version ${PROJECT_SOURCE_DIR}/FirebaseAuthInterop.podspec) + + objc_framework( + FirebaseAuthInterop + HEADERS ${headers} + VERSION ${version} + EXCLUDE_FROM_ALL + ) +endif() diff --git a/Interop/CoreDiagnostics/CMakeLists.txt b/Interop/CoreDiagnostics/CMakeLists.txt new file mode 100644 index 0000000000..5a8752dc7c --- /dev/null +++ b/Interop/CoreDiagnostics/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(APPLE) + file(GLOB headers Public/*.h) + + podspec_version(version ${PROJECT_SOURCE_DIR}/FirebaseCoreDiagnosticsInterop.podspec) + + objc_framework( + FirebaseCoreDiagnosticsInterop + HEADERS ${headers} + VERSION ${version} + INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} + EXCLUDE_FROM_ALL + ) +endif() diff --git a/cmake/cc_rules.cmake b/cmake/cc_rules.cmake index bf503d4a57..57cde7af01 100644 --- a/cmake/cc_rules.cmake +++ b/cmake/cc_rules.cmake @@ -29,23 +29,7 @@ function(cc_library name) cmake_parse_arguments(ccl "${flag}" "" "${multi}" ${ARGN}) if(ccl_HEADER_ONLY) - set(__empty_header_only_file "${CMAKE_CURRENT_BINARY_DIR}/${name}_header_only_empty.cc") - - if(NOT EXISTS ${__empty_header_only_file}) - file(WRITE ${__empty_header_only_file} - "// Generated file that keeps header-only CMake libraries happy. - - namespace firebase { - - // single meaningless symbol - void ${name}_header_only_fakesym() {} - - } // namespace firebase - " - ) - endif() - - list(APPEND ccl_SOURCES ${__empty_header_only_file}) + generate_dummy_source(${name} ccl_SOURCES) endif() maybe_remove_objc_sources(sources ${ccl_SOURCES}) @@ -273,3 +257,97 @@ function(add_alias ALIAS_TARGET ACTUAL_TARGET) add_library(${ALIAS_TARGET} ALIAS ${ACTUAL_TARGET}) endif() endfunction() + +# objc_framework( +# target +# HEADERS headers... +# SOURCES sources... +# INCLUDES inlude_directories... +# DEFINES macros... +# DEPENDS libraries... +# [EXCLUDE_FROM_ALL] +# ) +# +# Defines a new framework target with the given target name and parameters. +# +# If SOURCES is not included, a dummy file will be generated. +function(objc_framework target) + if(APPLE) + set(flag EXCLUDE_FROM_ALL) + set(single VERSION) + set(multi DEPENDS DEFINES HEADERS INCLUDES SOURCES) + cmake_parse_arguments(of "${flag}" "${single}" "${multi}" ${ARGN}) + + podspec_prep_headers(${target} ${of_HEADERS}) + + if (NOT cf_SOURCES) + generate_dummy_source(${target} of_SOURCES) + endif() + + add_library( + ${target} + STATIC + ${of_SOURCES} + ) + + set_property(TARGET ${target} PROPERTY PUBLIC_HEADER ${of_HEADERS}) + set_property(TARGET ${target} PROPERTY FRAMEWORK ON) + set_property(TARGET ${target} PROPERTY VERSION ${of_VERSION}) + + if(of_EXCLUDE_FROM_ALL) + set_property( + TARGET ${name} + PROPERTY EXCLUDE_FROM_ALL ON + ) + endif() + + target_compile_definitions( + ${target} + PUBLIC + ${of_DEFINES} + ) + + target_compile_options( + ${target} + INTERFACE + -F${CMAKE_CURRENT_BINARY_DIR} + PRIVATE + ${OBJC_FLAGS} + -fno-autolink + -Wno-unused-parameter + ) + + target_include_directories( + ${target} + PUBLIC ${PROJECT_BINARY_DIR}/Headers + PRIVATE ${of_INCLUDES} + ) + + target_link_libraries( + ${target} PUBLIC + ${of_DEPENDS} + ) + endif() +endfunction() + +# generate_dummy_source(name, sources_list) +# +# Generates a dummy source file containing a single symbol, suitable for use as +# a source file in when defining a header-only library. +# +# Appends the generated source file name to the list named by sources_list. +macro(generate_dummy_source name sources_list) + set(__empty_header_only_file "${CMAKE_CURRENT_BINARY_DIR}/${name}_header_only_empty.c") + + if(NOT EXISTS ${__empty_header_only_file}) + file(WRITE ${__empty_header_only_file} + "// Generated file that keeps header-only CMake libraries happy. + + // single meaningless symbol + void ${name}_header_only_fakesym(void) {} + " + ) + endif() + + list(APPEND ${sources_list} ${__empty_header_only_file}) +endmacro() \ No newline at end of file diff --git a/cmake/compiler_setup.cmake b/cmake/compiler_setup.cmake index 92129b4c73..06dab61610 100644 --- a/cmake/compiler_setup.cmake +++ b/cmake/compiler_setup.cmake @@ -43,11 +43,6 @@ if(CXX_CLANG OR CXX_GNU) set( cxx_flags - - # Cut down on symbol clutter - # TODO(wilhuff) try -fvisibility=hidden - -fvisibility-inlines-hidden - -Wreorder -Werror=reorder ) diff --git a/cmake/firebase_version.sed b/cmake/firebase_version.sed new file mode 100644 index 0000000000..a0de0b0831 --- /dev/null +++ b/cmake/firebase_version.sed @@ -0,0 +1,36 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# USAGE: +# sed -n -f cmake/firebase_version.sed FirebaseCore.podspec + +# Finds the Firebase version in the FirebaseCore podspec. Note this is *not* +# the FIRCore version. +# +# The line this looks for is this one: +# +# 'FIRCore_VERSION=' + s.version.to_s + ' Firebase_VERSION=6.5.0', + +# Find the line passing the Firebase_VERSION macro. +/.*Firebase_VERSION=/ { + # Re-use the pattern buffer to remove everything on the line up to and + # including the =. + s/// + + # Remove trailing quote and comma + s/',// + + # Explicitly print + p +} diff --git a/cmake/podspec_cmake.rb b/cmake/podspec_cmake.rb deleted file mode 100755 index 20e796afda..0000000000 --- a/cmake/podspec_cmake.rb +++ /dev/null @@ -1,515 +0,0 @@ -#!/usr/bin/env ruby - -# Copyright 2018 Google -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'cocoapods' -require 'fileutils' -require 'pathname' -require 'set' - -PLATFORM = :osx - -def usage() - script = File.basename($0) - STDERR.puts <<~EOF - USAGE: #{script} podspec cmake-file [subspecs...] - EOF -end - -def main(args) - if args.size < 2 then - usage() - exit(1) - end - - process(*args) -end - -# A CMake command, like add_library. The command name is stored in the first -# argument. -class CMakeCommand - # Create the command with its initial identifying arguments. - def initialize(*args) - @args = args - @checked_count = 0 - end - - def name() - return @args[0] - end - - def rest() - return @args[1..-1] - end - - def skip?() - return @checked_count == 0 - end - - def allow_missing_args() - @checked_count = nil - end - - # Adds the given arguments to the end of the command - def add_args(*args) - args = args.flatten - - unless @checked_count.nil? - @checked_count += args.size - end - - args.each do |arg| - unless @args.include?(arg) - @args.push(arg) - end - end - end -end - -# A model of a macOS or iOS Framework and the CMake commands required to build -# it. -class Framework - def initialize(name) - @name = name - @add_library = CMakeCommand.new('add_library', @name, 'STATIC') - @add_library.allow_missing_args() - - @public_headers = CMakeCommand.new( - 'set_property', 'TARGET', @name, 'PROPERTY', 'PUBLIC_HEADER') - - @properties = [] - - @extras = {} - end - - # Returns all the CMake commands required to build the framework. - def commands() - result = [@add_library] - result.push(@public_headers, *@properties) - @extras.keys.sort.each do |key| - result.push(@extras[key]) - end - return result - end - - # Adds library sources to the CMake add_library command that declares the - # library. - def add_sources(*sources) - @add_library.add_args(sources) - end - - # Adds public headers to the Framework - def add_public_headers(*headers) - @public_headers.add_args(headers) - end - - # Sets a target-level CMake property on the library target that declares the - # framework. - def set_property(property, *values) - command = CMakeCommand.new('set_property', 'TARGET', @name, 'PROPERTY', property) - command.add_args(values) - @properties.push(command) - end - - # Adds target-level preprocessor definitions. - # - # Args: - # - type: PUBLIC, PRIVATE, or INTERFACE - # - values: C preprocessor defintion arguments starting with -D - def compile_definitions(type, *values) - extra_command('target_compile_definitions', @name, type) - .add_args(values) - end - - # Adds target-level compile-time include path for the preprocessor. - # - # Args: - # - type: PUBLIC, PRIVATE, or INTERFACE - # - values: directory names, not including a leading -I flag - def include_directories(type, *dirs) - extra_command('target_include_directories', @name, type) - .add_args(dirs) - end - - # Adds target-level compile-time compiler options that aren't macro - # definitions or include directories. Link-time options should be added via - # lib_libraries. - # - # Args: - # - type: PUBLIC, PRIVATE, or INTERFACE - # - values: compiler flags, e.g. -fno-autolink - def compile_options(type, *values) - extra_command('target_compile_options', @name, type) - .add_args(values) - end - - # Adds target-level dependencies or link-time compiler options. CMake - # interprets any quoted string that starts with "-" as an option and anything - # else as a library target to depend upon. - # - # Args: - # - type: PUBLIC, PRIVATE, or INTERFACE - # - values: compiler flags, e.g. -fno-autolink - def link_libraries(type, *dirs) - extra_command('target_link_libraries', @name, type) - .add_args(dirs) - end - - private - def extra_command(*key_args) - key = key_args.join('|') - command = @extras[key] - if command.nil? - command = CMakeCommand.new(*key_args) - @extras[key] = command - end - return command - end -end - -# Generates a framework target based on podspec contents. Models the translation -# of a single podspec (and possible subspecs) to a single CMake framework -# target. -class CMakeGenerator - - # Initializes the generator with the given root Pod::Spec and the binary - # directory for the current CMake configuration. - # - # Args: - # - spec: A root specification, the name of which becomes the name of the - # Framework. - # - path_list: A Pod::Sandbox::PathList used to cache file operations. - # - cmake_binary_dir: A directory in which additional files may be written. - def initialize(spec, path_list, cmake_binary_dir) - @target = Framework.new(spec.name) - - headers_root = File.join(cmake_binary_dir, 'Headers') - @headers_dir = File.join(headers_root, spec.name) - - @root = spec - - @target.set_property('FRAMEWORK', 'ON') - @target.set_property('VERSION', spec.version) - - @target.include_directories('PRIVATE', headers_root, @headers_dir) - @target.link_libraries('PUBLIC', "\"-framework Foundation\"") - - root_dir = Pathname.new(__FILE__).expand_path().dirname().dirname() - @path_list = Pod::Sandbox::PathList.new(root_dir) - end - - attr_reader :target - - # Adds information from the given Pod::Spec to the definition of the CMake - # framework target. Subspecs are not automatically handled. - # - # Cocoapods subspecs are not independent libraries--they contribute sources - # and dependencies to a final single Framework. - # - # Args: - # - spec: A root or subspec that contributes to the final state of the of the - # Framework. - def add_framework(spec) - spec = spec.consumer(PLATFORM) - files = Pod::Sandbox::FileAccessor.new(@path_list, spec) - sources = [ - files.source_files, - files.public_headers, - files.private_headers, - ].flatten - @target.add_sources(sources) - - add_headers(files, sources) - - add_dependencies(spec) - add_framework_dependencies(spec) - - @target.compile_options('INTERFACE', '-F${CMAKE_CURRENT_BINARY_DIR}') - @target.compile_options('PRIVATE', '${OBJC_FLAGS}') - - add_xcconfig('PRIVATE', spec.pod_target_xcconfig) - add_xcconfig('PUBLIC', spec.user_target_xcconfig) - end - - private - # Sets up the framework headers so that compilation can succeed. - # Xcode/CocoaPods allow for several different include mechanisms to work: - # - # * Unqualified headers, e.g. +#import "FIRLoggerLevel.h"+, typically - # resolved via the header map. - # * Qualified relative to some source root, e.g. - # +#import "Public/FIRLoggerLevel.h"+, typically resolved by an include - # path - # * Framework imports, e.g. +#import +, - # resolved by a build process that copies headers into the framework - # structure. - # * Umbrella imports e.g. +#import + (which - # happens to import all the public headers). - # - # CMake's framework support is incomplete. It has no support at all for - # generating umbrella headers. - # - # CMake also does not completely support framework imports. It does work for - # sources outside the framework that want to build against it, but until the - # framework has been completely built the headers aren't available in this - # form. This prevents frameworks from referring to their own code via - # framework imports. - # - # This method cheats by creating a subdirectory in the build results that has - # symbolic links of all the public headers accessible with the right path. - # This makes it possible to use framework imports within the framework itself. - # The parent of this path is then added as a PRIVATE include directory of the - # target, making it possible for the framework to see itself this way. - def add_headers(files, sources) - # CMake-built frameworks don't have a notion of private headers, but they - # also don't have a notion of umbrella headers, so all framework headers - # need to be accessed by name. This means that just dumping all the private - # and public headers into what CMake considers the public headers makes - # everything work as we expect. - headers = [ - files.public_headers, - files.private_headers - ].flatten - - @target.add_public_headers(headers) - - # Also, link the headers into a directory that looks like a framework layout - # so that self-references via framework imports work. These *must* be - # symbolic links, otherwise our usual sloppiness causes file contents to be - # included multiple times, usually resulting in ambiguity errors. - FileUtils.mkdir_p(@headers_dir) - headers.each do |header| - FileUtils.ln_sf(header, File.join(@headers_dir, File.basename(header))) - end - - # Simulate header maps by adding include paths for all the directories - # containing non-public headers. - hmap_dirs = Set.new() - sources.each do |source| - next if File.extname(source) != '.h' - next if headers.include?(source) - - hmap_dirs.add(File.dirname(source)) - end - @target.include_directories('PRIVATE', *hmap_dirs.to_a.sort) - end - - # Account for differences in dependency names between CocoaPods and CMake. - # Keys should be CocoaPod dependency names and values should be the - # equivalent CMake library targets. - DEP_RENAMES = { - 'nanopb' => 'protobuf-nanopb-static' - } - - # Adds Pod::Spec +dependencies+ as target_link_libraries. Only root-specs are - # added as dependencies because in the CMake build there can be only one - # target for the framework. - def add_dependencies(spec) - prefix = "#{@root.name}/" - spec.dependencies.each do |dep| - # Dependencies on subspecs of this same spec are handled elsewhere. - next if dep.name.start_with?(prefix) - - name = dep.name.sub(/\/.*/, '') - name = DEP_RENAMES.fetch(name, name) - - @target.link_libraries('PUBLIC', name) - end - end - - # Adds target_link_libraries entries for all the items in the Pod::Spec - # +frameworks+ attribute. - def add_framework_dependencies(spec) - spec.frameworks.each do |framework| - @target.link_libraries('PUBLIC', "\"-framework #{framework}\"") - end - end - - # Mirrors known entries from the xcconfig entries into their equivalents in - # CMake. This translates OTHER_CFLAGS, GCC_PREPROCESSOR_DEFINITIONS, and - # HEADER_SEARCH_PATHS. - # - # Args: - # - type: PUBLIC for +pod_user_xcconfig+ or PRIVATE for - # +pod_target_xcconfig+. - # - xcconfig: the hash of xcconfig values. - def add_xcconfig(type, xcconfig) - if xcconfig.empty? - return - end - - @target.compile_options(type, split(xcconfig['OTHER_CFLAGS'])) - - defs = split(xcconfig['GCC_PREPROCESSOR_DEFINITIONS']) - defs = defs.map { |x| '-D' + x } - @target.compile_definitions(type, *defs) - - @target.include_directories(type, *split(xcconfig['HEADER_SEARCH_PATHS'])) - end - - # Splits a textual value in xcconfig. Always returns an array, but that array - # may be empty if the value didn't exist in the podspec. - def split(value) - if value.nil? - return [] - elsif value.kind_of?(String) - return value.split - else - return [value] - end - end -end - -# Processes a podspec file, translating all the specs within it into cmake file -# describing how to build it. -# -# Args: -# - podspec_file: The filename of the podspec to use as a source. -# - cmake_file: The filename of the cmake script to produce. -# - req_subspecs: Which subspecs to include. If empty, all subspecs are -# included (which corresponds to CocoaPods behavior. The default_subspec -# property is not handled. -def process(podspec_file, cmake_file, *req_subspecs) - root_dir = Pathname.new(__FILE__).expand_path().dirname().dirname() - path_list = Pod::Sandbox::PathList.new(root_dir) - - spec = Pod::Spec.from_file(podspec_file) - - writer = Writer.new() - writer.append <<~EOF - # This file was generated by #{File.basename(__FILE__)} - # from #{File.basename(podspec_file)}. - # Do not edit! - EOF - - cmake_binary_dir = File.expand_path(File.dirname(cmake_file)) - - gen = CMakeGenerator.new(spec, path_list, cmake_binary_dir) - gen.add_framework(spec) - - req_subspecs = normalize_requested_subspecs(spec, req_subspecs) - req_subspecs = resolve_subspec_deps(spec, req_subspecs) - - spec.subspecs.each do |subspec| - if req_subspecs.include?(subspec.name) - gen.add_framework(subspec) - end - end - - gen.target.commands.each do |command| - writer.write(command) - end - - File.open(cmake_file, 'w') do |fd| - fd.write(writer.result) - end -end - -# Returns true if test specifications are supported by the current version of -# CocoaPods and the given +spec+ is a test specification. -def test_specification?(spec) - # CocoaPods 1.3.0 added test specifications. - if !spec.respond_to?(:test_specification?) - return false - end - - return spec.test_specification? -end - -# Translates the (possibly empty) list of requested subspecs into the list of -# subspecs to actually include. If +req_subspecs+ is empty, returns all -# subspecs. If non-empty, all subspecs are returned as qualified names, e.g. -# "Logger" may become "GoogleUtilities/Logger". -def normalize_requested_subspecs(spec, req_subspecs) - if req_subspecs.empty? - subspecs = spec.subspecs.select { |s| not test_specification?(s) } - return subspecs.map { |s| s.name } - else - return req_subspecs.map do |name| - if name.include?(?/) - name - else - "#{spec.name}/#{name}" - end - end - end -end - -# Expands the list of requested subspecs to include any dependencies within the -# same root subspec. For example, if +req_subspecs+ where -# -# +["GoogleUtilties/Logger"]+, -# -# the result would be -# -# +["GoogleUtilties/Logger", "GoogleUtilities/Environment"]+ -# -# because Logger depends upon Environment within the same root spec. -def resolve_subspec_deps(spec, req_subspecs) - prefix = spec.name + '/' - - result = Set.new() - while !req_subspecs.empty? - req = req_subspecs.pop - result.add(req) - - subspec = spec.subspec_by_name(req) - subspec.dependencies(PLATFORM).each do |dep| - if dep.name.start_with?(prefix) && !result.include?(dep.name) - req_subspecs.push(dep.name) - end - end - end - - return result.to_a.sort -end - -# Writes CMake commands out to textual form, taking care of line wrapping. -class Writer - def initialize() - @last_command = nil - @result = "" - end - - attr_reader :result - - def write(command) - if command.skip? - return - end - - if command.name != @last_command - @result << "\n" - end - @last_command = command.name - - single = "#{command.name}(#{command.rest.join(' ')})\n" - if single.size < 80 - @result << single - else - @result << "#{command.name}(\n" - command.rest.each do |arg| - @result << " #{arg}\n" - end - @result << ")\n" - end - end - - def append(text) - @result << text - end -end - -main(ARGV) diff --git a/cmake/podspec_rules.cmake b/cmake/podspec_rules.cmake index 530ee31852..d8791a2900 100644 --- a/cmake/podspec_rules.cmake +++ b/cmake/podspec_rules.cmake @@ -14,55 +14,32 @@ include(CMakeParseArguments) -# Reads properties from the given podspec and generates a cmake file that -# defines the equivalent framework. -# -# Only does anything useful on Apple platforms. On non-Apple platforms, this -# function has no effect--no target is created. -macro(podspec_framework PODSPEC_FILE) - if(APPLE) - set(multi SPECS) - cmake_parse_arguments(psf "" "" "${multi}" ${ARGN}) - - get_filename_component(_pod_name ${PODSPEC_FILE} NAME_WE) - set(_properties_file ${_pod_name}.cmake) - - # Use bundler only if the current source tree has it set up. Otherwise fall - # back on the system ruby setup, which may have CocoaPods installed. - set(psf_runner ruby) - if(EXISTS ${FIREBASE_SOURCE_DIR}/.bundle) - set(psf_runner bundle exec) - endif() - - execute_process( - COMMAND - ${psf_runner} - ${FIREBASE_SOURCE_DIR}/cmake/podspec_cmake.rb - ${PODSPEC_FILE} - ${CMAKE_CURRENT_BINARY_DIR}/${_properties_file} - ${psf_SPECS} - ) - - # Get CMake to automatically re-run if the generation script or the podspec - # source changes. - set_property( - DIRECTORY APPEND PROPERTY - CMAKE_CONFIGURE_DEPENDS ${FIREBASE_SOURCE_DIR}/cmake/podspec_cmake.rb - ) - set_property( - DIRECTORY APPEND PROPERTY - CMAKE_CONFIGURE_DEPENDS ${PODSPEC_FILE} - ) - - include(${CMAKE_CURRENT_BINARY_DIR}/${_properties_file}) - - # Non-Firestore Objective-C code in this repository is not as strict about - # warnings. - target_compile_options( - ${_pod_name} - PRIVATE - -Wno-unused-parameter - -Wno-deprecated-declarations - ) - endif() +macro(podspec_version VARIABLE PODSPEC_FILE) + execute_process( + COMMAND + sed -n + -f ${PROJECT_SOURCE_DIR}/cmake/podspec_version.sed + ${PODSPEC_FILE} + OUTPUT_VARIABLE ${VARIABLE} + OUTPUT_STRIP_TRAILING_WHITESPACE + ) endmacro() + +macro(firebase_version VARIABLE PODSPEC_FILE) + execute_process( + COMMAND + sed -n + -f ${PROJECT_SOURCE_DIR}/cmake/firebase_version.sed + ${PODSPEC_FILE} + OUTPUT_VARIABLE ${VARIABLE} + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endmacro() + +function(podspec_prep_headers FRAMEWORK_NAME) + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/Headers/${FRAMEWORK_NAME}) + execute_process( + COMMAND + ln -sf ${ARGN} ${PROJECT_BINARY_DIR}/Headers/${FRAMEWORK_NAME} + ) +endfunction() diff --git a/cmake/podspec_version.sed b/cmake/podspec_version.sed new file mode 100644 index 0000000000..2ea8aef0a6 --- /dev/null +++ b/cmake/podspec_version.sed @@ -0,0 +1,32 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# USAGE: +# sed -n -f cmake/podspec_version.sed foo.podspec + +# Finds the version of a podspec, looking for a line line this: +# +# s.version = '6.1.0' + +# Find the line assigning to s.version +/.*s\.version *= */ { + # Reuse the pattern buffer to remove the assignment. + s/// + + # Remove the surrounding quotes + s/'\([^']*\)'/\1/ + + # Explicitly print + p +}