-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathbuild_zips.py
812 lines (713 loc) · 27.9 KB
/
build_zips.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
#!/usr/bin/python
#
# Copyright 2022 Google LLC
#
# 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.
"""Build SDK for certain platform into zip file
Example usage:
python build_zips.py --platform=macos --apis=auth --targets=firestore
"""
import glob
import os
import shutil
import subprocess
import zipfile
import tempfile
import threading
import sys
from absl import app, flags, logging
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"]
IOS_DEVICE_ARCHITECTURE = ["arm64", "armv7"]
IOS_SIMULATOR_ARCHITECTURE = ["arm64", "x86_64", "i386"]
IOS_CONFIG_DICT = {
"device": {
"architecture": ["arm64", "armv7"],
"ios_platform_location": "iPhoneOS.platform",
"osx_sysroot": "iphoneos",
},
"simulator": {
"architecture": ["arm64", "x86_64", "i386"],
"ios_platform_location": "iPhoneSimulator.platform",
"osx_sysroot": "iphonesimulator",
}
}
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"]
g_target_architectures = []
g_cpp_sdk_realpath = ""
FLAGS = flags.FLAGS
flags.DEFINE_string(
'platform', None,
'Which platform to build SDK on. Required one entry from ({})'.format(
",".join(SUPPORT_PLATFORMS)))
flags.DEFINE_string(
'unity_root', None,
"The root dir for Unity Engine. If not set, cmake will try to guess in the default unity installation location."
)
flags.DEFINE_multi_string(
"targets", None,
("Target product to includes in the build. List items pick from"
"({})".format(",".join(SUPPORT_TARGETS))))
flags.DEFINE_string(
"apis", None,
("One single string that has multiple targets combined with ','. \n"
"Usage example is --apis=auth,firestore,remote_config. \n"
"Exclusive with parameter targets. List items pick from"
"({})".format(",".join(SUPPORT_TARGETS))))
flags.DEFINE_multi_string(
"device", None,
"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. Ignored on tvOS.\n"
"For iOS device ({}).\n"
"For iOS simulator ({}).\n"
"For android ({}).\n"
"For MacOS ({})".format(",".join(IOS_CONFIG_DICT["device"]["architecture"]),
",".join(
IOS_CONFIG_DICT["simulator"]["architecture"]),
",".join(ANDROID_SUPPORT_ARCHITECTURE),
",".join(MACOS_SUPPORT_ARCHITECTURE)))
flags.DEFINE_multi_string('cmake_extras', None,
"Any extra arguments wants to pass into cmake.")
flags.DEFINE_bool("clean_build", False, "Whether to clean the build folder")
flags.DEFINE_bool("use_boringssl", False, "Build with BoringSSL instead of openSSL.")
flags.DEFINE_bool("verbose", False, "If verbose, cmake build with DCMAKE_VERBOSE_MAKEFILE=1")
flags.DEFINE_string("swig_dir", None, "If pass in swig dir directly rather than find swig by cmake")
flags.DEFINE_bool("gen_documentation_zip", False, "Also generate a zip file containing files to document")
flags.DEFINE_bool("gha", False, "True if the build is triggered by Github Action.")
def get_build_path(platform, clean_build=False):
"""Get the folder that cmake configure and build in.
Args:
platform: linux, macos, windows, ios, android.
clean_build: If True, delete the build folder and build from clean.
Returns:
The folder path to build sdk inside.
"""
platform_path = os.path.join(os.getcwd(), platform + "_unity")
if os.path.exists(platform_path) and clean_build:
shutil.rmtree(platform_path)
if not os.path.exists(platform_path):
os.makedirs(platform_path)
return platform_path
def get_cpp_folder_args(source_path):
"""Get the cmake args to pass in local Firebase C++ SDK folder.
If not found, will download from Firebase C++ git repo.
Args:
source_path: root source folder cd back.
Returns:
cmake args with the folder path of local Firebase C++ SDK.
Empty string if not found.
"""
global g_cpp_sdk_realpath
cpp_folder = os.path.join(os.getcwd(), "..", "firebase-cpp-sdk")
if os.path.exists(cpp_folder):
g_cpp_sdk_realpath = os.path.realpath(cpp_folder)
return "-DFIREBASE_CPP_SDK_DIR=" + g_cpp_sdk_realpath
else:
return ""
def get_unity_engine_folder_args(unity_root):
"""Get the cmake args to pass in Unity engine folder. If not passed in
through the parameter, cmake will try to find using logic in
cmake/FindUnity.cmake
Args:
unity_root: folder path of the Unity Engine.
Returns:
camke args with the folder path of Unity Engine. Empty string if not set.
"""
if unity_root and os.path.exists(unity_root):
return "-DUNITY_ROOT_DIR=" + unity_root
else:
return ""
def get_targets_args(targets):
"""Get the cmake args to pass in built targets of Firebase products.
Args:
targets: list of target names defined in SUPPORT_TARGETS.
Returns:
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:
raise app.UsageError(
'Wrong target "{}", please pick from {}'.format(
target, ",".join(support_targets)))
for target in SUPPORT_TARGETS:
if target in targets:
result_args.append("-DFIREBASE_INCLUDE_" + target.upper() +
"=ON")
else:
result_args.append("-DFIREBASE_INCLUDE_" + target.upper() +
"=OFF")
logging.debug("get target args are:" + ",".join(result_args))
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.
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_ios.cmake")
# toolchain args is required
result_args.append("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_path)
# check device input
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)))
devices = FLAGS.device
else:
devices = SUPPORT_DEVICE
global g_target_architectures
# check architecture input
if (len(devices) > 1):
archs_to_check = IOS_SUPPORT_ARCHITECTURE
else:
archs_to_check = IOS_CONFIG_DICT[devices[0]]["architecture"]
if FLAGS.architecture:
for arch in FLAGS.architecture:
if arch not in archs_to_check:
raise app.UsageError(
'Wrong architecture "{}" for device type {}, please pick from {}'.format(
arch, ",".join(devices), ",".join(archs_to_check)))
g_target_architectures = FLAGS.architecture
else:
g_target_architectures = archs_to_check
if len(g_target_architectures) != len(IOS_SUPPORT_ARCHITECTURE):
# Need to override only if the archs are not default
result_args.append("-DCMAKE_OSX_ARCHITECTURES=" +
";".join(g_target_architectures))
if len(devices) != len(SUPPORT_DEVICE):
# Need to override if only passed in device or simulator
result_args.append("-DCMAKE_OSX_SYSROOT=" +
IOS_CONFIG_DICT[devices[0]]["osx_sysroot"])
result_args.append("-DCMAKE_XCODE_EFFECTIVE_PLATFORMS=" +
"-"+IOS_CONFIG_DICT[devices[0]]["osx_sysroot"])
result_args.append("-DIOS_PLATFORM_LOCATION=" +
IOS_CONFIG_DICT[devices[0]]["ios_platform_location"])
return result_args
def get_android_args():
"""Get the cmake args for android platform specific.
Returns:
camke args for android platform.
"""
result_args = []
# get Android NDK path
system_android_ndk_home = os.getenv('ANDROID_NDK_HOME')
if system_android_ndk_home:
toolchain_path = os.path.join(
system_android_ndk_home, "build", "cmake", "android.toolchain.cmake")
result_args.append("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_path)
logging.info("Use ANDROID_NDK_HOME(%s) cmake toolchain(%s)",
system_android_ndk_home, toolchain_path)
else:
system_android_home = os.getenv('ANDROID_HOME')
if system_android_home:
toolchain_files = glob.glob(os.path.join(system_android_home,
"**", "build", "cmake", "android.toolchain.cmake"), recursive=True)
if toolchain_files:
result_args.append("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_files[0])
logging.info("Use ANDROID_HOME(%s) cmake toolchain (%s)",
system_android_home, toolchain_files[0])
else:
raise app.UsageError(
'Neither ANDROID_NDK_HOME nor ANDROID_HOME is set.')
# get architecture setup
global g_target_architectures
if FLAGS.architecture:
for arch in FLAGS.architecture:
if arch not in ANDROID_SUPPORT_ARCHITECTURE:
raise app.UsageError(
'Wrong architecture "{}", please pick from {}'.format(
arch, ",".join(ANDROID_SUPPORT_ARCHITECTURE)))
g_target_architectures = FLAGS.architecture
else:
g_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
if len(g_target_architectures) == 1:
result_args.append("-DANDROID_ABI="+g_target_architectures[0])
result_args.append("-DFIREBASE_ANDROID_BUILD=true")
# android default to build release.
result_args.append("-DCMAKE_BUILD_TYPE=release")
result_args.append("-DANDROID_STL=c++_shared")
return result_args
def make_android_multi_arch_build(cmake_args, merge_script):
"""Make android build for different architectures, and then combine them together.
Args:
cmake_args: cmake arguments used to build each architecture.
merge_script: script path to merge the srcaar files.
"""
global g_target_architectures
current_folder = os.getcwd()
# build multiple archictures
for arch in g_target_architectures:
if not os.path.exists(arch):
os.makedirs(arch)
build_dir = os.path.join(current_folder, arch)
cmake_args.append("-DANDROID_ABI="+arch)
subprocess.call(cmake_args, cwd=build_dir)
subprocess.call("make", cwd=build_dir)
cmake_pack_args = [
"cpack",
".",
]
subprocess.call(cmake_pack_args, cwd=build_dir)
# merge them
zip_base_name = ""
srcarr_list = []
base_temp_dir = tempfile.mkdtemp()
for arch in g_target_architectures:
# find *Android.zip in subfolder architecture
arch_zip_path = glob.glob(os.path.join(arch, "*Android.zip"))
if not arch_zip_path:
logging.error("No *Android.zip generated for architecture %s", arch)
return
if not zip_base_name:
# first architecture, so extract to the final temp folder. The following
# srcaar 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)
srcarr_list.extend(glob.glob(os.path.join(
base_temp_dir, "**", "*.srcaar"), recursive=True))
else:
temporary_dir = tempfile.mkdtemp()
# from the second *Android.zip, we only need to extract *.srcaar files to operate the merge.
with zipfile.ZipFile(arch_zip_path[0]) as zip_file:
for file in zip_file.namelist():
if file.endswith('.srcaar'):
zip_file.extract(file, temporary_dir)
logging.debug("Unpacked file %s from zip file %s to %s",
file, arch_zip_path, temporary_dir)
for srcaar_file in srcarr_list:
srcaar_name = os.path.basename(srcaar_file)
matching_files = glob.glob(os.path.join(
temporary_dir, "**", "*"+srcaar_name), recursive=True)
if matching_files:
merge_args = [
"python",
merge_script,
"--inputs=" + srcaar_file,
"--inputs=" + matching_files[0],
"--output=" + srcaar_file,
]
subprocess.call(merge_args)
logging.debug("merging %s to %s", matching_files[0], srcaar_file)
# achive the temp folder to the final firebase_unity-<version>-Android.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 Android multi-arch (%s) zip %s",
",".join(g_target_architectures), final_zip_path)
def get_windows_args():
"""Get the cmake args for windows platform specific.
Returns:
cmake args for windows platform.
"""
result_args = []
result_args.append('-G Visual Studio 16 2019')
result_args.append('-A x64') # TODO flexibily for x32
result_args.append("-DFIREBASE_PYTHON_HOST_EXECUTABLE:FILEPATH=%s" % sys.executable)
return result_args
def get_macos_args():
"""Get the cmake args for macos platform specific.
Returns:
cmake args for macos platform.
"""
result_args = []
global g_target_architectures
# get architecture setup global g_target_architectures
if FLAGS.architecture:
for arch in FLAGS.architecture:
if arch not in MACOS_SUPPORT_ARCHITECTURE:
raise app.UsageError(
'Wrong architecture "{}", please pick from {}'.format(
arch, ",".join(MACOS_SUPPORT_ARCHITECTURE)))
g_target_architectures = FLAGS.architecture
else:
# Default to selecting none, as it will likely only be able to build the local architecture.
g_target_architectures = []
if len(g_target_architectures) == 1:
result_args.append('-DCMAKE_OSX_ARCHITECTURES='+g_target_architectures[0])
return result_args
def make_macos_arch(arch, cmake_args):
"""Make the macos build for the given architecture.
Assumed to be called from the build directory.
Args:
arch: The architecture to build for.
cmake_args: Additional cmake arguments to use.
"""
if not os.path.exists(arch):
os.makedirs(arch)
build_dir = os.path.join(os.getcwd(), arch)
cmake_args.append('-DCMAKE_OSX_ARCHITECTURES='+arch)
subprocess.call(cmake_args, cwd=build_dir)
subprocess.call(['make'], cwd=build_dir)
subprocess.call(['cpack', '.'], cwd=build_dir)
def make_macos_multi_arch_build(cmake_args):
"""Make macos build for different architectures, and then combine them together
Args:
cmake_args: cmake arguments used to build each architecture.
"""
global g_target_architectures
# build multiple architectures
current_folder = os.getcwd()
threads = []
for arch in g_target_architectures:
t = threading.Thread(target=make_macos_arch, args=(arch, cmake_args))
t.start()
threads.append(t)
for t in threads:
t.join()
# Merge the different zip files together, using lipo on the bundle files
zip_base_name = ""
bundle_list = []
base_temp_dir = tempfile.mkdtemp()
for arch in g_target_architectures:
# find *Darwin.zip in subfolder architecture
arch_zip_path = glob.glob(os.path.join(arch, "*Darwin.zip"))
if not arch_zip_path:
logging.error("No *Darwin.zip generated for architecture %s", arch)
return
if not zip_base_name:
# first architecture, so extract to the final temp folder. The following
# bundle 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)
bundle_list.extend(glob.glob(os.path.join(
base_temp_dir, "**", "*.bundle"), recursive=True))
else:
temporary_dir = tempfile.mkdtemp()
# from the second *Darwin.zip, we only need to extract *.bundle files to operate the merge.
with zipfile.ZipFile(arch_zip_path[0]) as zip_file:
for file in zip_file.namelist():
if file.endswith('.bundle'):
zip_file.extract(file, temporary_dir)
logging.debug("Unpacked file %s from zip file %s to %s",
file, arch_zip_path, temporary_dir)
for bundle_file in bundle_list:
bundle_name = os.path.basename(bundle_file)
matching_files = glob.glob(os.path.join(
temporary_dir, "**", "*"+bundle_name), recursive=True)
if matching_files:
merge_args = [
"lipo",
bundle_file,
matching_files[0],
"-create",
"-output",
bundle_file,
]
subprocess.call(merge_args)
logging.debug("merging %s to %s", matching_files[0], bundle_file)
# achive the temp folder to the final firebase_unity-<version>-Darwin.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 (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.
"""
if not FLAGS.gen_documentation_zip:
return
cpack_args = [
'cpack',
'-D', 'CPACK_COMPONENTS_ALL=documentation',
'-D', 'CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE=ON',
'-D', 'CPACK_ARCHIVE_FILE_NAME=documentation_sources'
]
subprocess.call(cpack_args)
def is_android_build():
"""
Returns:
If the build platform is android
"""
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:
If the build platform is windows
"""
return FLAGS.platform == "windows"
def is_macos_build():
"""
Returns:
If the build platform is macos
"""
return FLAGS.platform == "macos"
def is_linux_build():
"""
Returns:
If the build platform is linux
"""
return FLAGS.platform == "linux"
def main(argv):
if len(argv) > 1:
raise app.UsageError('Too many command-line arguments.')
platform = FLAGS.platform
if platform not in SUPPORT_PLATFORMS:
raise app.UsageError('Wrong platform "{}", please pick from {}'.format(
platform, ",".join(SUPPORT_PLATFORMS)))
source_path = os.getcwd()
cmake_cpp_folder_args = get_cpp_folder_args(source_path)
build_path = get_build_path(platform, FLAGS.clean_build)
if is_android_build() and g_cpp_sdk_realpath:
# For android build, if we find local cpp folder,
# We trigger the cpp android build first.
subprocess.call("./gradlew", cwd=g_cpp_sdk_realpath)
os.chdir(build_path)
cmake_setup_args = [
"cmake",
source_path
]
if FLAGS.verbose:
cmake_setup_args.append('-DCMAKE_VERBOSE_MAKEFILE=1')
if is_windows_build():
# windows args need to happen right after target path
cmake_setup_args.extend(get_windows_args())
cmake_setup_args.extend([
"-DFIREBASE_INCLUDE_UNITY=ON",
"-DFIREBASE_UNITY_BUILD_TESTS=ON",
"-DFIREBASE_CPP_BUILD_STUB_TESTS=ON",
])
if FLAGS.gha:
cmake_setup_args.append("-DFIREBASE_GITHUB_ACTION_BUILD=ON")
unity_root_args = get_unity_engine_folder_args(FLAGS.unity_root)
if unity_root_args:
cmake_setup_args.append(unity_root_args)
if cmake_cpp_folder_args:
cmake_setup_args.append(cmake_cpp_folder_args)
if FLAGS.apis and FLAGS.targets:
raise app.UsageError('Parameter apis and targets are exclusive, please only pick one.')
api_target_list = []
if FLAGS.apis:
api_target_list = FLAGS.apis.split(',')
elif FLAGS.targets:
api_target_list = FLAGS.targets
target_arg_list = get_targets_args(api_target_list)
if FLAGS.swig_dir:
cmake_setup_args.append("-DSWIG_DIR="+FLAGS.swig_dir)
if target_arg_list:
cmake_setup_args.extend(target_arg_list)
if FLAGS.cmake_extras:
cmake_setup_args.extend(FLAGS.cmake_extras)
if FLAGS.use_boringssl:
cmake_setup_args.append("-DFIREBASE_USE_BORINGSSL=ON")
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():
cmake_setup_args.extend(get_macos_args())
global g_target_architectures
logging.info("cmake_setup_args is: " + " ".join(cmake_setup_args))
if is_android_build() and len(g_target_architectures) > 1:
logging.info("Build android with multiple architectures %s",
",".join(g_target_architectures))
# android multi architecture build is a bit different
make_android_multi_arch_build(cmake_setup_args, os.path.join(
source_path, "aar_builder", "merge_aar.py"))
elif is_macos_build() and len(g_target_architectures) > 1:
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():
# no make command in windows. TODO make config passable
subprocess.call("cmake --build . --config Release")
else:
subprocess.call(["make"])
cmake_pack_args = [
"cpack",
".",
]
subprocess.call(cmake_pack_args)
gen_documentation_zip()
os.chdir(source_path)
if __name__ == '__main__':
flags.mark_flag_as_required("platform")
app.run(main)