Skip to content

Commit 592145d

Browse files
authored
tvOS build capability added to build_zips (#465)
Breaking ground on tvOS support. This PR updates the cmake build system and the build_zips.py script to generate fat libs for tvOS simulators and devices.
1 parent a085deb commit 592145d

File tree

5 files changed

+248
-10
lines changed

5 files changed

+248
-10
lines changed

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ else()
106106
set(FIREBASE_SYSTEM_DEPS)
107107
endif()
108108

109-
if(NOT FIREBASE_IOS_BUILD) # besides iOS build, don't need lib prefix
109+
# besides iOS/tvOS builds don't need lib prefix.
110+
if(NOT FIREBASE_IOS_BUILD)
110111
set(CMAKE_SHARED_LIBRARY_PREFIX "")
111112
set(CMAKE_STATIC_LIBRARY_PREFIX "")
112113
endif()
@@ -243,6 +244,11 @@ if (DESKTOP AND APPLE AND FIREBASE_INCLUDE_EDITOR_TOOL)
243244
add_subdirectory(editor)
244245
endif()
245246

247+
if (PLATFORM STREQUAL TVOS OR PLATFORM STREQUAL SIMULATOR_TVOS)
248+
# Dynamic Links is not supported on tvOS.
249+
set(FIREBASE_INCLUDE_DYNAMIC_LINKS OFF)
250+
endif()
251+
246252
if (FIREBASE_INCLUDE_ANALYTICS)
247253
add_subdirectory(analytics)
248254
list(APPEND TARGET_LINK_LIB_NAMES "firebase_analytics" "firebase_analytics_swig")

cmake/build_shared.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function(build_firebase_shared LIBRARY_NAME ARTIFACT_NAME OUTPUT_NAME)
3636
set(shared_target "firebase_${LIBRARY_NAME}_shared")
3737

3838
if(FIREBASE_IOS_BUILD OR NOT FIREBASE_UNI_LIBRARY)
39-
# On iOS, we want to include all the symbols in the library
39+
# On iOS and tvOS, we want to include all the symbols in the library
4040
add_library(${shared_target} SHARED
4141
${FIREBASE_SOURCE_DIR}/empty.cc
4242
$<TARGET_OBJECTS:firebase_${LIBRARY_NAME}>

cmake/unity_pack.cmake

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ include(unity_mono)
2424

2525
# iOS will get the platform name of Darwin if we dont change it here
2626
if(FIREBASE_IOS_BUILD)
27-
set(CPACK_SYSTEM_NAME "iOS")
27+
if(FIREBASE_TVOS_VARIANT)
28+
set(CPACK_SYSTEM_NAME "tvOS")
29+
else()
30+
set(CPACK_SYSTEM_NAME "iOS")
31+
endif()
2832
endif()
2933

3034
set(CPACK_GENERATOR "ZIP")
@@ -35,7 +39,11 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
3539
include(CPack)
3640

3741
if(FIREBASE_IOS_BUILD)
38-
set(UNITY_PACK_NATIVE_DIR "Plugins/iOS/Firebase")
42+
if(FIREBASE_TVOS_VARIANT)
43+
set(UNITY_PACK_NATIVE_DIR "Plugins/tvOS/Firebase")
44+
else()
45+
set(UNITY_PACK_NATIVE_DIR "Plugins/iOS/Firebase")
46+
endif()
3947
elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64" OR
4048
"${CMAKE_GENERATOR_PLATFORM}" STREQUAL "")
4149
set(UNITY_PACK_NATIVE_DIR "Firebase/Plugins/x86_64")

cmake/unity_tvos.cmake

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright 2022 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
message(STATUS "gcc found at: ${CMAKE_C_COMPILER}")
16+
message(STATUS "g++ found at: ${CMAKE_CXX_COMPILER}")
17+
message(STATUS "Using iOS SDK: ${CMAKE_OSX_SYSROOT}")
18+
19+
set(CMAKE_SYSTEM_NAME "tvOS")
20+
21+
set(CMAKE_OSX_SYSROOT "")
22+
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "")
23+
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-appletvos;-tvsimulator")
24+
set(IOS_PLATFORM_LOCATION "iPhoneOS.platform;iPhoneSimulator.platform")
25+
set(IOS_PLATFORM_LOCATION "AppleTvOS.platform;AppleTvSimulator.platform")
26+
27+
set(PLATFORM_INT "${PLATFORM}")
28+
if(PLATFORM_INT STREQUAL "TVOS")
29+
set(SDK_NAME appletvos)
30+
set(ARCHS arm64)
31+
set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos)
32+
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64")
33+
elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS")
34+
set(SDK_NAME appletvsimulator)
35+
set(ARCHS x86_64)
36+
set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos)
37+
set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "x86_64")
38+
endif()
39+
40+
if(NOT DEFINED ENABLE_ARC)
41+
message(STATUS "ARC not defined, enabling by default")
42+
set(ENABLE_ARC TRUE)
43+
endif()
44+
45+
set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" FORCE)
46+
set(XCODE_IOS_PLATFORM tvos)
47+
48+
set(CMAKE_IOS_INSTALL_UNIVERSAL_LIBS "YES")
49+
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO")
50+
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
51+
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
52+
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.1" CACHE STRING "")
53+
54+
# skip TRY_COMPILE checks
55+
set(CMAKE_CXX_COMPILER_WORKS TRUE)
56+
set(CMAKE_C_COMPILER_WORKS TRUE)
57+
58+
set(FIREBASE_IOS_BUILD ON CACHE BOOL "")
59+
set(IOS ON CACHE BOOL "")
60+
set(FIREBASE_TVOS_VARIANT ON CACHE BOOL "")

scripts/build_scripts/build_zips.py

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
1818
1919
Example usage:
20-
python build_zips.py --platform=macos --targets=auth --targets=firestore
20+
python build_zips.py --platform=macos --apis=auth --targets=firestore
2121
"""
2222
import glob
2323
import os
@@ -30,12 +30,16 @@
3030

3131
from absl import app, flags, logging
3232

33-
SUPPORT_PLATFORMS = ("linux", "macos", "windows", "ios", "android")
33+
SUPPORT_PLATFORMS = ("linux", "macos", "windows", "ios", "tvos", "android")
3434
SUPPORT_TARGETS = [
3535
"analytics", "auth", "crashlytics", "database", "dynamic_links",
3636
"firestore", "functions", "installations", "messaging", "remote_config",
3737
"storage"
3838
]
39+
TVOS_SUPPORT_TARGETS = [
40+
"analytics", "auth", "crashlytics", "database", "firestore", "functions",
41+
"installations", "messaging", "remote_config", "storage"
42+
]
3943
SUPPORT_DEVICE = ["device", "simulator"]
4044

4145
IOS_SUPPORT_ARCHITECTURE = ["arm64", "armv7", "x86_64", "i386"]
@@ -55,6 +59,21 @@
5559
}
5660
}
5761

62+
TVOS_CONFIG_DICT = {
63+
"device": {
64+
"architecture": ["arm64"],
65+
"ios_platform_location": "AppleTvOS.platform",
66+
"osx_sysroot": "appletvos",
67+
"toolchain_platform": "TVOS",
68+
},
69+
"simulator": {
70+
"architecture": ["x86_64"],
71+
"ios_platform_location": "AppleTvSimulator.platform",
72+
"osx_sysroot": "appletvsimulator",
73+
"toolchain_platform": "SIMULATOR_TVOS",
74+
}
75+
}
76+
5877
ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
5978

6079
MACOS_SUPPORT_ARCHITECTURE = ["x86_64", "arm64"]
@@ -86,7 +105,7 @@
86105
"To build on device or simulator. If not set, built on both. Only take affect for ios and android build"
87106
)
88107
flags.DEFINE_multi_string(
89-
"architecture", None, "Which architectures in build on.\n"
108+
"architecture", None, "Which architectures in build on. Ignored on tvOS.\n"
90109
"For iOS device ({}).\n"
91110
"For iOS simulator ({}).\n"
92111
"For android ({}).\n"
@@ -167,13 +186,20 @@ def get_targets_args(targets):
167186
camke args included targets.
168187
"""
169188
result_args = []
189+
190+
support_targets = SUPPORT_TARGETS
191+
if is_tvos_build():
192+
support_targets = TVOS_SUPPORT_TARGETS
193+
if not targets:
194+
targets = TVOS_SUPPORT_TARGETS
195+
170196
if targets:
171197
# check if all the entries are valid
172198
for target in targets:
173-
if target not in SUPPORT_TARGETS:
199+
if target not in support_targets:
174200
raise app.UsageError(
175201
'Wrong target "{}", please pick from {}'.format(
176-
target, ",".join(SUPPORT_TARGETS)))
202+
target, ",".join(support_targets)))
177203
for target in SUPPORT_TARGETS:
178204
if target in targets:
179205
result_args.append("-DFIREBASE_INCLUDE_" + target.upper() +
@@ -185,6 +211,31 @@ def get_targets_args(targets):
185211
return result_args
186212

187213

214+
def get_tvos_args(source_path):
215+
"""Get the cmake args for tvOS platforms.
216+
217+
Args:
218+
source_path: root source folder to find toolchain file.
219+
Returns:
220+
camke args for iOS platform.
221+
"""
222+
result_args = []
223+
toolchain_path = os.path.join(source_path, "cmake", "unity_tvos.cmake")
224+
result_args.append("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_path)
225+
# check device input
226+
global g_target_devices
227+
if FLAGS.device:
228+
for device in FLAGS.device:
229+
if device not in SUPPORT_DEVICE:
230+
raise app.UsageError(
231+
'Wrong device type {}, please pick from {}'.format(
232+
device, ",".join(SUPPORT_DEVICE)))
233+
g_target_devices = FLAGS.device
234+
else:
235+
g_target_devices = SUPPORT_DEVICE
236+
237+
return result_args
238+
188239
def get_ios_args(source_path):
189240
"""Get the cmake args for iOS platform specific.
190241
@@ -490,6 +541,109 @@ def make_macos_multi_arch_build(cmake_args):
490541
logging.info("Generated Darwin (MacOS) multi-arch (%s) zip %s",
491542
",".join(g_target_architectures), final_zip_path)
492543

544+
545+
def make_tvos_target(device, arch, cmake_args):
546+
"""Make the tvos build for the given device and architecture.
547+
Assumed to be called from the build directory.
548+
549+
Args:
550+
arch: The architecture to build for.
551+
cmake_args: Additional cmake arguments to use.
552+
"""
553+
build_args = cmake_args.copy()
554+
build_args.append("-DCMAKE_OSX_ARCHITECTURES=" + arch)
555+
build_args.append("-DCMAKE_OSX_SYSROOT=" +
556+
TVOS_CONFIG_DICT[device]["osx_sysroot"])
557+
build_args.append("-DCMAKE_XCODE_EFFECTIVE_PLATFORMS=" +
558+
"-"+TVOS_CONFIG_DICT[device]["osx_sysroot"])
559+
build_args.append("-DIOS_PLATFORM_LOCATION=" +
560+
TVOS_CONFIG_DICT[device]["ios_platform_location"])
561+
build_args.append("-DPLATFORM=" +
562+
TVOS_CONFIG_DICT[device]["toolchain_platform"])
563+
564+
if not os.path.exists(arch):
565+
os.makedirs(arch)
566+
build_dir = os.path.join(os.getcwd(), arch)
567+
subprocess.call(build_args, cwd=build_dir)
568+
subprocess.call('make', cwd=build_dir)
569+
subprocess.call(['cpack', '.'], cwd=build_dir)
570+
571+
def make_tvos_multi_arch_build(cmake_args):
572+
"""Make tvos build for different architectures, and then combine
573+
them together into a fat libraries and a single zip file.
574+
575+
Args:
576+
cmake_args: cmake arguments used to build each architecture.
577+
"""
578+
global g_target_devices
579+
current_folder = os.getcwd()
580+
target_architectures = []
581+
582+
# build multiple architectures
583+
current_folder = os.getcwd()
584+
threads = []
585+
for device in g_target_devices:
586+
for arch in TVOS_CONFIG_DICT[device]["architecture"]:
587+
target_architectures.append(arch)
588+
t = threading.Thread(target=make_tvos_target, args=(device, arch, cmake_args))
589+
t.start()
590+
threads.append(t)
591+
592+
for t in threads:
593+
t.join()
594+
595+
# Merge the different zip files together, using lipo on the library files
596+
zip_base_name = ""
597+
library_list = []
598+
base_temp_dir = tempfile.mkdtemp()
599+
for arch in target_architectures:
600+
# find *.zip in subfolder architecture
601+
arch_zip_path = glob.glob(os.path.join(arch, "*-tvOS.zip"))
602+
if not arch_zip_path:
603+
logging.error("No *-tvOS.zip generated for architecture %s", arch)
604+
return
605+
if not zip_base_name:
606+
# first architecture, so extract to the final temp folder. The following
607+
# library files will merge to the ones in this folder.
608+
zip_base_name = arch_zip_path[0]
609+
with zipfile.ZipFile(zip_base_name) as zip_file:
610+
zip_file.extractall(base_temp_dir)
611+
library_list.extend(glob.glob(os.path.join(
612+
base_temp_dir, "**", "*.a"), recursive=True))
613+
else:
614+
temporary_dir = tempfile.mkdtemp()
615+
# from the second *-tvOS.zip, we only need to extract *.a files to operate the merge.
616+
with zipfile.ZipFile(arch_zip_path[0]) as zip_file:
617+
for file in zip_file.namelist():
618+
if file.endswith('.a'):
619+
zip_file.extract(file, temporary_dir)
620+
621+
for library_file in library_list:
622+
library_name = os.path.basename(library_file)
623+
matching_files = glob.glob(os.path.join(
624+
temporary_dir, "Plugins", "tvOS", "Firebase", library_name))
625+
if matching_files:
626+
merge_args = [
627+
"lipo",
628+
library_file,
629+
matching_files[0],
630+
"-create",
631+
"-output",
632+
library_file,
633+
]
634+
subprocess.call(merge_args)
635+
logging.info("merging %s to %s", matching_files[0], library_name)
636+
637+
# archive the temp folder to the final firebase_unity-<version>-tvOS.zip
638+
final_zip_path = os.path.join(current_folder, os.path.basename(zip_base_name))
639+
with zipfile.ZipFile(final_zip_path, "w", allowZip64=True) as zip_file:
640+
for current_root, _, filenames in os.walk(base_temp_dir):
641+
for filename in filenames:
642+
fullpath = os.path.join(current_root, filename)
643+
zip_file.write(fullpath, os.path.relpath(fullpath, base_temp_dir))
644+
logging.info("Generated Darwin (tvOS) multi-arch (%s) zip %s",
645+
",".join(g_target_architectures), final_zip_path)
646+
493647
def gen_documentation_zip():
494648
"""If the flag was enabled, builds the zip file containing source files to document.
495649
"""
@@ -510,14 +664,20 @@ def is_android_build():
510664
"""
511665
return FLAGS.platform == "android"
512666

513-
514667
def is_ios_build():
515668
"""
516669
Returns:
517670
If the build platform is ios
518671
"""
519672
return FLAGS.platform == "ios"
520673

674+
def is_tvos_build():
675+
"""
676+
Returns:
677+
If the build platform is tvos
678+
"""
679+
return FLAGS.platform == "tvos"
680+
521681
def is_windows_build():
522682
"""
523683
Returns:
@@ -608,6 +768,8 @@ def main(argv):
608768

609769
if is_ios_build():
610770
cmake_setup_args.extend(get_ios_args(source_path))
771+
elif is_tvos_build():
772+
cmake_setup_args.extend(get_tvos_args(source_path))
611773
elif is_android_build():
612774
cmake_setup_args.extend(get_android_args())
613775
elif is_macos_build():
@@ -625,6 +787,8 @@ def main(argv):
625787
logging.info("Build macos with multiple architectures %s",
626788
",".join(g_target_architectures))
627789
make_macos_multi_arch_build(cmake_setup_args)
790+
elif is_tvos_build():
791+
make_tvos_multi_arch_build(cmake_setup_args)
628792
else:
629793
subprocess.call(cmake_setup_args)
630794
if is_windows_build():

0 commit comments

Comments
 (0)