Skip to content

tvOS build capability added to build_zips #465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ else()
set(FIREBASE_SYSTEM_DEPS)
endif()

if(NOT FIREBASE_IOS_BUILD) # besides iOS build, don't need lib prefix
# besides iOS/tvOS builds don't need lib prefix.
if(NOT FIREBASE_IOS_BUILD)
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(CMAKE_STATIC_LIBRARY_PREFIX "")
endif()
Expand Down Expand Up @@ -243,6 +244,11 @@ if (DESKTOP AND APPLE AND FIREBASE_INCLUDE_EDITOR_TOOL)
add_subdirectory(editor)
endif()

if (PLATFORM STREQUAL TVOS OR PLATFORM STREQUAL SIMULATOR_TVOS)
# Dynamic Links is not supported on tvOS.
set(FIREBASE_INCLUDE_DYNAMIC_LINKS OFF)
endif()

if (FIREBASE_INCLUDE_ANALYTICS)
add_subdirectory(analytics)
list(APPEND TARGET_LINK_LIB_NAMES "firebase_analytics" "firebase_analytics_swig")
Expand Down
2 changes: 1 addition & 1 deletion cmake/build_shared.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function(build_firebase_shared LIBRARY_NAME ARTIFACT_NAME OUTPUT_NAME)
set(shared_target "firebase_${LIBRARY_NAME}_shared")

if(FIREBASE_IOS_BUILD OR NOT FIREBASE_UNI_LIBRARY)
# On iOS, we want to include all the symbols in the library
# On iOS and tvOS, we want to include all the symbols in the library
add_library(${shared_target} SHARED
${FIREBASE_SOURCE_DIR}/empty.cc
$<TARGET_OBJECTS:firebase_${LIBRARY_NAME}>
Expand Down
12 changes: 10 additions & 2 deletions cmake/unity_pack.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ include(unity_mono)

# iOS will get the platform name of Darwin if we dont change it here
if(FIREBASE_IOS_BUILD)
set(CPACK_SYSTEM_NAME "iOS")
if(FIREBASE_TVOS_VARIANT)
set(CPACK_SYSTEM_NAME "tvOS")
else()
set(CPACK_SYSTEM_NAME "iOS")
endif()
endif()

set(CPACK_GENERATOR "ZIP")
Expand All @@ -35,7 +39,11 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
include(CPack)

if(FIREBASE_IOS_BUILD)
set(UNITY_PACK_NATIVE_DIR "Plugins/iOS/Firebase")
if(FIREBASE_TVOS_VARIANT)
set(UNITY_PACK_NATIVE_DIR "Plugins/tvOS/Firebase")
else()
set(UNITY_PACK_NATIVE_DIR "Plugins/iOS/Firebase")
endif()
elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64" OR
"${CMAKE_GENERATOR_PLATFORM}" STREQUAL "")
set(UNITY_PACK_NATIVE_DIR "Firebase/Plugins/x86_64")
Expand Down
60 changes: 60 additions & 0 deletions cmake/unity_tvos.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2022 Google Inc. All rights reserved.
#
# 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.

message(STATUS "gcc found at: ${CMAKE_C_COMPILER}")
message(STATUS "g++ found at: ${CMAKE_CXX_COMPILER}")
message(STATUS "Using iOS SDK: ${CMAKE_OSX_SYSROOT}")

set(CMAKE_SYSTEM_NAME "tvOS")

set(CMAKE_OSX_SYSROOT "")
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "")
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-appletvos;-tvsimulator")
set(IOS_PLATFORM_LOCATION "iPhoneOS.platform;iPhoneSimulator.platform")
set(IOS_PLATFORM_LOCATION "AppleTvOS.platform;AppleTvSimulator.platform")

set(PLATFORM_INT "${PLATFORM}")
if(PLATFORM_INT STREQUAL "TVOS")
set(SDK_NAME appletvos)
set(ARCHS arm64)
set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos)
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64")
elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS")
set(SDK_NAME appletvsimulator)
set(ARCHS x86_64)
set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos)
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "x86_64")
endif()

if(NOT DEFINED ENABLE_ARC)
message(STATUS "ARC not defined, enabling by default")
set(ENABLE_ARC TRUE)
endif()

set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" FORCE)
set(XCODE_IOS_PLATFORM tvos)

set(CMAKE_IOS_INSTALL_UNIVERSAL_LIBS "YES")
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO")
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.1" CACHE STRING "")

# skip TRY_COMPILE checks
set(CMAKE_CXX_COMPILER_WORKS TRUE)
set(CMAKE_C_COMPILER_WORKS TRUE)

set(FIREBASE_IOS_BUILD ON CACHE BOOL "")
set(IOS ON CACHE BOOL "")
set(FIREBASE_TVOS_VARIANT ON CACHE BOOL "")
176 changes: 170 additions & 6 deletions scripts/build_scripts/build_zips.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@


Example usage:
python build_zips.py --platform=macos --targets=auth --targets=firestore
python build_zips.py --platform=macos --apis=auth --targets=firestore
"""
import glob
import os
Expand All @@ -30,12 +30,16 @@

from absl import app, flags, logging

SUPPORT_PLATFORMS = ("linux", "macos", "windows", "ios", "android")
SUPPORT_PLATFORMS = ("linux", "macos", "windows", "ios", "tvos", "android")
SUPPORT_TARGETS = [
"analytics", "auth", "crashlytics", "database", "dynamic_links",
"firestore", "functions", "installations", "messaging", "remote_config",
"storage"
]
TVOS_SUPPORT_TARGETS = [
"analytics", "auth", "crashlytics", "database", "firestore", "functions",
"installations", "messaging", "remote_config", "storage"
]
SUPPORT_DEVICE = ["device", "simulator"]

IOS_SUPPORT_ARCHITECTURE = ["arm64", "armv7", "x86_64", "i386"]
Expand All @@ -55,6 +59,21 @@
}
}

TVOS_CONFIG_DICT = {
"device": {
"architecture": ["arm64"],
"ios_platform_location": "AppleTvOS.platform",
"osx_sysroot": "appletvos",
"toolchain_platform": "TVOS",
},
"simulator": {
"architecture": ["x86_64"],
"ios_platform_location": "AppleTvSimulator.platform",
"osx_sysroot": "appletvsimulator",
"toolchain_platform": "SIMULATOR_TVOS",
}
}

ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]

MACOS_SUPPORT_ARCHITECTURE = ["x86_64", "arm64"]
Expand Down Expand Up @@ -86,7 +105,7 @@
"To build on device or simulator. If not set, built on both. Only take affect for ios and android build"
)
flags.DEFINE_multi_string(
"architecture", None, "Which architectures in build on.\n"
"architecture", None, "Which architectures in build on. Ignored on tvOS.\n"
"For iOS device ({}).\n"
"For iOS simulator ({}).\n"
"For android ({}).\n"
Expand Down Expand Up @@ -167,13 +186,20 @@ def get_targets_args(targets):
camke args included targets.
"""
result_args = []

support_targets = SUPPORT_TARGETS
if is_tvos_build():
support_targets = TVOS_SUPPORT_TARGETS
if not targets:
targets = TVOS_SUPPORT_TARGETS

if targets:
# check if all the entries are valid
for target in targets:
if target not in SUPPORT_TARGETS:
if target not in support_targets:
raise app.UsageError(
'Wrong target "{}", please pick from {}'.format(
target, ",".join(SUPPORT_TARGETS)))
target, ",".join(support_targets)))
for target in SUPPORT_TARGETS:
if target in targets:
result_args.append("-DFIREBASE_INCLUDE_" + target.upper() +
Expand All @@ -185,6 +211,31 @@ def get_targets_args(targets):
return result_args


def get_tvos_args(source_path):
"""Get the cmake args for tvOS platforms.

Args:
source_path: root source folder to find toolchain file.
Returns:
camke args for iOS platform.
"""
result_args = []
toolchain_path = os.path.join(source_path, "cmake", "unity_tvos.cmake")
result_args.append("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_path)
# check device input
global g_target_devices
if FLAGS.device:
for device in FLAGS.device:
if device not in SUPPORT_DEVICE:
raise app.UsageError(
'Wrong device type {}, please pick from {}'.format(
device, ",".join(SUPPORT_DEVICE)))
g_target_devices = FLAGS.device
else:
g_target_devices = SUPPORT_DEVICE

return result_args

def get_ios_args(source_path):
"""Get the cmake args for iOS platform specific.

Expand Down Expand Up @@ -490,6 +541,109 @@ def make_macos_multi_arch_build(cmake_args):
logging.info("Generated Darwin (MacOS) multi-arch (%s) zip %s",
",".join(g_target_architectures), final_zip_path)


def make_tvos_target(device, arch, cmake_args):
"""Make the tvos build for the given device and architecture.
Assumed to be called from the build directory.

Args:
arch: The architecture to build for.
cmake_args: Additional cmake arguments to use.
"""
build_args = cmake_args.copy()
build_args.append("-DCMAKE_OSX_ARCHITECTURES=" + arch)
build_args.append("-DCMAKE_OSX_SYSROOT=" +
TVOS_CONFIG_DICT[device]["osx_sysroot"])
build_args.append("-DCMAKE_XCODE_EFFECTIVE_PLATFORMS=" +
"-"+TVOS_CONFIG_DICT[device]["osx_sysroot"])
build_args.append("-DIOS_PLATFORM_LOCATION=" +
TVOS_CONFIG_DICT[device]["ios_platform_location"])
build_args.append("-DPLATFORM=" +
TVOS_CONFIG_DICT[device]["toolchain_platform"])

if not os.path.exists(arch):
os.makedirs(arch)
build_dir = os.path.join(os.getcwd(), arch)
subprocess.call(build_args, cwd=build_dir)
subprocess.call('make', cwd=build_dir)
subprocess.call(['cpack', '.'], cwd=build_dir)

def make_tvos_multi_arch_build(cmake_args):
"""Make tvos build for different architectures, and then combine
them together into a fat libraries and a single zip file.

Args:
cmake_args: cmake arguments used to build each architecture.
"""
global g_target_devices
current_folder = os.getcwd()
target_architectures = []

# build multiple architectures
current_folder = os.getcwd()
threads = []
for device in g_target_devices:
for arch in TVOS_CONFIG_DICT[device]["architecture"]:
target_architectures.append(arch)
t = threading.Thread(target=make_tvos_target, args=(device, arch, cmake_args))
t.start()
threads.append(t)

for t in threads:
t.join()

# Merge the different zip files together, using lipo on the library files
zip_base_name = ""
library_list = []
base_temp_dir = tempfile.mkdtemp()
for arch in target_architectures:
# find *.zip in subfolder architecture
arch_zip_path = glob.glob(os.path.join(arch, "*-tvOS.zip"))
if not arch_zip_path:
logging.error("No *-tvOS.zip generated for architecture %s", arch)
return
if not zip_base_name:
# first architecture, so extract to the final temp folder. The following
# library files will merge to the ones in this folder.
zip_base_name = arch_zip_path[0]
with zipfile.ZipFile(zip_base_name) as zip_file:
zip_file.extractall(base_temp_dir)
library_list.extend(glob.glob(os.path.join(
base_temp_dir, "**", "*.a"), recursive=True))
else:
temporary_dir = tempfile.mkdtemp()
# from the second *-tvOS.zip, we only need to extract *.a files to operate the merge.
with zipfile.ZipFile(arch_zip_path[0]) as zip_file:
for file in zip_file.namelist():
if file.endswith('.a'):
zip_file.extract(file, temporary_dir)

for library_file in library_list:
library_name = os.path.basename(library_file)
matching_files = glob.glob(os.path.join(
temporary_dir, "Plugins", "tvOS", "Firebase", library_name))
if matching_files:
merge_args = [
"lipo",
library_file,
matching_files[0],
"-create",
"-output",
library_file,
]
subprocess.call(merge_args)
logging.info("merging %s to %s", matching_files[0], library_name)

# archive the temp folder to the final firebase_unity-<version>-tvOS.zip
final_zip_path = os.path.join(current_folder, os.path.basename(zip_base_name))
with zipfile.ZipFile(final_zip_path, "w", allowZip64=True) as zip_file:
for current_root, _, filenames in os.walk(base_temp_dir):
for filename in filenames:
fullpath = os.path.join(current_root, filename)
zip_file.write(fullpath, os.path.relpath(fullpath, base_temp_dir))
logging.info("Generated Darwin (tvOS) multi-arch (%s) zip %s",
",".join(g_target_architectures), final_zip_path)

def gen_documentation_zip():
"""If the flag was enabled, builds the zip file containing source files to document.
"""
Expand All @@ -510,14 +664,20 @@ def is_android_build():
"""
return FLAGS.platform == "android"


def is_ios_build():
"""
Returns:
If the build platform is ios
"""
return FLAGS.platform == "ios"

def is_tvos_build():
"""
Returns:
If the build platform is tvos
"""
return FLAGS.platform == "tvos"

def is_windows_build():
"""
Returns:
Expand Down Expand Up @@ -608,6 +768,8 @@ def main(argv):

if is_ios_build():
cmake_setup_args.extend(get_ios_args(source_path))
elif is_tvos_build():
cmake_setup_args.extend(get_tvos_args(source_path))
elif is_android_build():
cmake_setup_args.extend(get_android_args())
elif is_macos_build():
Expand All @@ -625,6 +787,8 @@ def main(argv):
logging.info("Build macos with multiple architectures %s",
",".join(g_target_architectures))
make_macos_multi_arch_build(cmake_setup_args)
elif is_tvos_build():
make_tvos_multi_arch_build(cmake_setup_args)
else:
subprocess.call(cmake_setup_args)
if is_windows_build():
Expand Down