mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
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:
14 files changed
+331
-663
No files matched your search
+4
-60
@@ -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)
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
@@ -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()
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user