Rework CMake builds of Firebase Frameworks (#3499)

* Add sed scripts that find the versions of podspecs

... without fully loading those via Pod::Spec, which is slow.

* Avoid compiling with different visibility settings

... in different translation units. This removes warnings about linking
googletest causing weak symbols becoming strong.

* Add CMake objc_framework

* Define FirebaseCore via objc_framework

* Define FIRCoreDiagnosticsInterop via objc_framework

* Define GoogleUtilities with objc_framework

* Define FirebaseCoreDiagnosticsInterop via objc_framework

* Define FirebaseAuthInterop via objc_framework

* Don't duplicate Core's dependency information everywhere

* Remove podspec_framework

* Fix compilation error with -Wstrict-prototyes
This commit is contained in:
Gil authored and GitHub committed 2019-08-05 16:46:28 -07:00
1 parent 9bfbb9ece0
commit 766e8723d7
14 files changed
+331 -663

No files matched your search

+4 -60
View File
@@ -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)
+40
View File
@@ -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()
@@ -34,11 +34,6 @@ cc_library(
DEPENDS
FirebaseAuthInterop
FirebaseCore
FirebaseCoreDiagnostics
FirebaseCoreDiagnosticsInterop
GoogleDataTransport
GoogleDataTransportCCTSupport
GoogleUtilities
firebase_firestore_auth_base
EXCLUDE_FROM_ALL
)
@@ -94,11 +94,6 @@ cc_library(
string_format.h
DEPENDS
FirebaseCore
FirebaseCoreDiagnostics
FirebaseCoreDiagnosticsInterop
GoogleDataTransport
GoogleDataTransportCCTSupport
GoogleUtilities
absl_base
absl_strings
EXCLUDE_FROM_ALL
@@ -20,11 +20,6 @@ cc_library(
xcgmock.h
DEPENDS
FirebaseCore
FirebaseCoreDiagnostics
FirebaseCoreDiagnosticsInterop
GoogleDataTransport
GoogleDataTransportCCTSupport
GoogleUtilities
absl_strings
EXCLUDE_FROM_ALL
)
+43
View File
@@ -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()
+26
View File
@@ -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()
+27
View File
@@ -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()
+95 -17
View File
@@ -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()
-5
View File
@@ -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
)
+36
View File
@@ -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
}
-515
View File
@@ -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 <FirebaseCore/FIRLoggerLevel.h>+,
# resolved by a build process that copies headers into the framework
# structure.
# * Umbrella imports e.g. +#import <FirebaseCore/FirebaseCore.h>+ (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)
+28 -51
View File
@@ -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()
+32
View File
@@ -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
}