aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGil <mcg@google.com>2018-07-16 11:46:55 -0700
committerGitHub <noreply@github.com>2018-07-16 11:46:55 -0700
commitc7fb0a487523b3686a03e3b7f9b459fcb9323a36 (patch)
treeb2a7ddbb1ecf7f354b000642f195129223e4bf81
parent22562f49ebd39bb7f3d3b00f020841eb1f545934 (diff)
Generate CMake frameworks from podspecs (#1531)
* Rename utils.cmake to cc_rules.cmake This makes it less of a dumping ground * Fix sign mismatch in FIRApp * Implement a podspec_framework CMake function ... that generates a CMake framework library target from a podspec. * Remove manual CMake scripts for xcodebuild
-rw-r--r--Firebase/Core/FIRApp.m2
-rw-r--r--Firestore/CMakeLists.txt17
-rw-r--r--Firestore/Example/Firestore.xcodeproj/project.pbxproj2
-rw-r--r--cmake/FindFirebaseCore.cmake56
-rw-r--r--cmake/FindGoogleUtilities.cmake56
-rw-r--r--cmake/cc_rules.cmake (renamed from cmake/utils.cmake)0
-rw-r--r--cmake/external/FirebaseCore.cmake27
-rw-r--r--cmake/external/GoogleUtilities.cmake27
-rw-r--r--cmake/external/firestore.cmake4
-rwxr-xr-xcmake/podspec_cmake.rb495
-rw-r--r--cmake/podspec_rules.cmake51
-rw-r--r--cmake/xcodebuild.cmake89
12 files changed, 560 insertions, 266 deletions
diff --git a/Firebase/Core/FIRApp.m b/Firebase/Core/FIRApp.m
index 5fca127..f06185a 100644
--- a/Firebase/Core/FIRApp.m
+++ b/Firebase/Core/FIRApp.m
@@ -166,7 +166,7 @@ static NSMutableDictionary *sLibraryVersions;
if ([name isEqualToString:kFIRDefaultAppName]) {
[NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be __FIRAPP_DEFAULT."];
}
- for (NSInteger charIndex = 0; charIndex < name.length; charIndex++) {
+ for (NSUInteger charIndex = 0; charIndex < name.length; charIndex++) {
char character = [name characterAtIndex:charIndex];
if (!((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') ||
(character >= '0' && character <= '9') || character == '_' || character == '-')) {
diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt
index 039cb06..831d8ab 100644
--- a/Firestore/CMakeLists.txt
+++ b/Firestore/CMakeLists.txt
@@ -34,14 +34,11 @@ set(FIREBASE_BINARY_DIR ${PROJECT_BINARY_DIR}/..)
list(INSERT CMAKE_MODULE_PATH 0 ${FIREBASE_SOURCE_DIR}/cmake)
include(SanitizerOptions)
-include(utils)
+include(cc_rules)
+include(podspec_rules)
# External packages
-if(APPLE)
- find_package(FirebaseCore REQUIRED)
- find_package(GoogleUtilities REQUIRED)
-endif()
find_package(LevelDB REQUIRED)
find_package(ZLIB)
@@ -128,6 +125,16 @@ target_include_directories(
enable_testing()
include(CompilerSetup)
+# Firebase packages
+podspec_framework(
+ ${FIREBASE_SOURCE_DIR}/GoogleUtilities.podspec
+ SPECS Logger
+)
+
+podspec_framework(
+ ${FIREBASE_SOURCE_DIR}/FirebaseCore.podspec
+)
+
# Superbuild installed results
include_directories(${FIREBASE_INSTALL_DIR}/include)
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
index ddd9e83..3a5d30a 100644
--- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj
+++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj
@@ -903,9 +903,9 @@
6EDD3B5D20BF24A700C33877 /* FuzzTests */ = {
isa = PBXGroup;
children = (
- 6ED6DEA120F5502700FC6076 /* FuzzingResources */,
6EDD3B5E20BF24D000C33877 /* FSTFuzzTestsPrincipal.mm */,
6EDD3B5C20BF247500C33877 /* Firestore_FuzzTests_iOS-Info.plist */,
+ 6ED6DEA120F5502700FC6076 /* FuzzingResources */,
);
path = FuzzTests;
sourceTree = "<group>";
diff --git a/cmake/FindFirebaseCore.cmake b/cmake/FindFirebaseCore.cmake
deleted file mode 100644
index eec29dd..0000000
--- a/cmake/FindFirebaseCore.cmake
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright 2017 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.
-
-find_library(
- FIREBASECORE_LIBRARY
- FirebaseCore
- PATHS ${FIREBASE_INSTALL_DIR}/Frameworks
-)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(
- FirebaseCore
- DEFAULT_MSG
- FIREBASECORE_LIBRARY
-)
-
-if(FIREBASECORE_FOUND)
- # Emulate CocoaPods behavior which makes all headers available unqualified.
- set(
- FIREBASECORE_INCLUDE_DIRS
- ${FIREBASECORE_LIBRARY}/Headers
- ${FIREBASECORE_LIBRARY}/PrivateHeaders
- )
-
- set(
- FIREBASECORE_LIBRARIES
- ${FIREBASECORE_LIBRARY}
- "-framework Foundation"
- )
-
- if(NOT TARGET FirebaseCore)
- # Add frameworks as INTERFACE libraries rather than IMPORTED so that
- # framework behavior is preserved.
- add_library(FirebaseCore INTERFACE)
-
- set_property(
- TARGET FirebaseCore APPEND PROPERTY
- INTERFACE_INCLUDE_DIRECTORIES ${FIREBASECORE_INCLUDE_DIRS}
- )
- set_property(
- TARGET FirebaseCore APPEND PROPERTY
- INTERFACE_LINK_LIBRARIES ${FIREBASECORE_LIBRARIES}
- )
- endif()
-endif(FIREBASECORE_FOUND)
diff --git a/cmake/FindGoogleUtilities.cmake b/cmake/FindGoogleUtilities.cmake
deleted file mode 100644
index 37f7361..0000000
--- a/cmake/FindGoogleUtilities.cmake
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-
-find_library(
- GoogleUtilities_LIBRARY
- GoogleUtilities
- PATHS ${FIREBASE_INSTALL_DIR}/Frameworks
-)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(
- GoogleUtilities
- DEFAULT_MSG
- GoogleUtilities_LIBRARY
-)
-
-if(GoogleUtilities_FOUND)
- # Emulate CocoaPods behavior which makes all headers available unqualified.
- set(
- GoogleUtilities_INCLUDE_DIRS
- ${GoogleUtilities_LIBRARY}/Headers
- ${GoogleUtilities_LIBRARY}/PrivateHeaders
- )
-
- set(
- GoogleUtilities_LIBRARIES
- ${GoogleUtilities_LIBRARY}
- "-framework Foundation"
- )
-
- if(NOT TARGET GoogleUtilities)
- # Add frameworks as INTERFACE libraries rather than IMPORTED so that
- # framework behavior is preserved.
- add_library(GoogleUtilities INTERFACE)
-
- set_property(
- TARGET GoogleUtilities APPEND PROPERTY
- INTERFACE_INCLUDE_DIRECTORIES ${GoogleUtilities_INCLUDE_DIRS}
- )
- set_property(
- TARGET GoogleUtilities APPEND PROPERTY
- INTERFACE_LINK_LIBRARIES ${GoogleUtilities_LIBRARIES}
- )
- endif()
-endif(GoogleUtilities_FOUND)
diff --git a/cmake/utils.cmake b/cmake/cc_rules.cmake
index 7d32624..7d32624 100644
--- a/cmake/utils.cmake
+++ b/cmake/cc_rules.cmake
diff --git a/cmake/external/FirebaseCore.cmake b/cmake/external/FirebaseCore.cmake
deleted file mode 100644
index a14397e..0000000
--- a/cmake/external/FirebaseCore.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright 2017 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.
-
-include(xcodebuild)
-
-if(TARGET FirebaseCore)
- return()
-endif()
-
-if(APPLE)
- # FirebaseCore is only available as a CocoaPod build.
- xcodebuild(FirebaseCore)
-else()
- # On non-Apple platforms, there's no way to build FirebaseCore.
- add_custom_target(FirebaseCore)
-endif()
diff --git a/cmake/external/GoogleUtilities.cmake b/cmake/external/GoogleUtilities.cmake
deleted file mode 100644
index a7a9ce8..0000000
--- a/cmake/external/GoogleUtilities.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-include(xcodebuild)
-
-if(TARGET GoogleUtilities)
- return()
-endif()
-
-if(APPLE)
- # GoogleUtilities is only available as a CocoaPod build.
- xcodebuild(GoogleUtilities)
-else()
- # On non-Apple platforms, there's no way to build GoogleUtilities.
- add_custom_target(GoogleUtilities)
-endif()
diff --git a/cmake/external/firestore.cmake b/cmake/external/firestore.cmake
index d91a543..2409c50 100644
--- a/cmake/external/firestore.cmake
+++ b/cmake/external/firestore.cmake
@@ -13,8 +13,6 @@
# limitations under the License.
include(ExternalProject)
-include(external/FirebaseCore)
-include(external/GoogleUtilities)
include(external/googletest)
include(external/grpc)
include(external/leveldb)
@@ -28,8 +26,6 @@ endif()
ExternalProject_Add(
Firestore
DEPENDS
- FirebaseCore
- GoogleUtilities
googletest
grpc
leveldb
diff --git a/cmake/podspec_cmake.rb b/cmake/podspec_cmake.rb
new file mode 100755
index 0000000..4063f35
--- /dev/null
+++ b/cmake/podspec_cmake.rb
@@ -0,0 +1,495 @@
+#!/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
+
+ # 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(/\/.*/, '')
+ @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
+
+# 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)
+ subspecs = spec.subspecs
+ if req_subspecs.empty?
+ 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
new file mode 100644
index 0000000..043c1a2
--- /dev/null
+++ b/cmake/podspec_rules.cmake
@@ -0,0 +1,51 @@
+# 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.
+
+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(_properties_file ${PODSPEC_FILE} NAME_WE)
+ set(_properties_file ${_properties_file}.cmake)
+
+ execute_process(
+ COMMAND
+ ${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})
+ endif()
+endmacro()
diff --git a/cmake/xcodebuild.cmake b/cmake/xcodebuild.cmake
deleted file mode 100644
index cc72c60..0000000
--- a/cmake/xcodebuild.cmake
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright 2017 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.
-
-include(CMakeParseArguments)
-include(ExternalProject)
-
-# Builds an existing Xcode project or workspace as an external project in CMake.
-#
-# xcodebuild(<framework> [<option>...])
-#
-# Options:
-# ``DEPENDS <projects>...``
-# Targets on which the project depends
-# ``SCHEME <scheme>``
-# The scheme to build in the workspace, defaults to <framework>-<platform>,
-# where <platform> is always "macOS".
-# ``WORKSPACE <workspace>``
-# Location of the xcworkspace file containing the target to build. Defaults to
-# Example/Firebase.xcworkspace.
-function(xcodebuild framework)
- # Parse arguments
- set(options "")
- set(single_value SCHEME WORKSPACE)
- set(multi_value DEPENDS)
- cmake_parse_arguments(xcb "${options}" "${single_value}" "${multi_value}" ${ARGN})
-
- if(NOT xcb_WORKSPACE)
- set(xcb_WORKSPACE ${PROJECT_SOURCE_DIR}/Example/Firebase.xcworkspace)
- endif()
-
- # TODO(mcg): Investigate supporting non-macOS platforms
- # The canonical way to build and test for iOS is via Xcode and CocoaPods so
- # it's not super important to make this work here
- set(platform macOS)
- set(destination "platform=macOS,arch=x86_64")
- set(scheme "${framework}-${platform}")
-
- # CMake has a variety of release types, but Xcode has just one by default.
- if(CMAKE_BUILD_TYPE STREQUAL Debug)
- set(configuration Debug)
- else()
- set(configuration Release)
- endif()
-
- # Pipe build output through xcpretty if it's available
- find_program(xcpretty_cmd xcpretty)
- if(xcpretty_cmd)
- set(pipe_xcpretty "|" ${xcpretty_cmd})
- endif()
-
- ExternalProject_Add(
- ${framework}
- DEPENDS ${xcb_DEPENDS}
-
- PREFIX ${PROJECT_BINARY_DIR}
-
- # The source directory doesn't actually matter
- SOURCE_DIR ${PROJECT_SOURCE_DIR}
- INSTALL_DIR ${FIREBASE_INSTALL_DIR}/Frameworks
-
- CONFIGURE_COMMAND ""
-
- BUILD_COMMAND
- xcodebuild
- -workspace ${xcb_WORKSPACE}
- -scheme ${scheme}
- -configuration ${configuration}
- -destination ${destination}
- CONFIGURATION_BUILD_DIR=<INSTALL_DIR>
- build
- ${pipe_xcpretty}
- BUILD_ALWAYS ${BUILD_PODS}
-
- INSTALL_COMMAND ""
- TEST_COMMAND ""
- )
-
-endfunction()