25
25
import subprocess
26
26
import zipfile
27
27
import tempfile
28
+ import threading
28
29
import sys
29
30
30
31
from absl import app , flags , logging
56
57
57
58
ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a" , "arm64-v8a" , "x86" , "x86_64" ]
58
59
59
- g_mobile_target_architectures = []
60
+ MACOS_SUPPORT_ARCHITECTURE = ["x86_64" , "arm64" ]
61
+
62
+ g_target_architectures = []
60
63
g_cpp_sdk_realpath = ""
61
64
62
65
FLAGS = flags .FLAGS
80
83
"architecture" , None , "Which architectures in build on.\n "
81
84
"For iOS device ({}).\n "
82
85
"For iOS simulator ({}).\n "
83
- "For android ({})." .format ("," .join (IOS_CONFIG_DICT ["device" ]["architecture" ]),
86
+ "For android ({}).\n "
87
+ "For MacOS ({})" .format ("," .join (IOS_CONFIG_DICT ["device" ]["architecture" ]),
84
88
"," .join (
85
89
IOS_CONFIG_DICT ["simulator" ]["architecture" ]),
86
- "," .join (ANDROID_SUPPORT_ARCHITECTURE )))
90
+ "," .join (ANDROID_SUPPORT_ARCHITECTURE ),
91
+ "," .join (MACOS_SUPPORT_ARCHITECTURE )))
87
92
flags .DEFINE_multi_string ('cmake_extras' , None ,
88
93
"Any extra arguments wants to pass into cmake." )
89
94
flags .DEFINE_bool ("clean_build" , False , "Whether to clean the build folder" )
90
-
95
+ flags . DEFINE_bool ( "use_boringssl" , False , "Build with BoringSSL instead of openSSL." )
91
96
92
97
def get_build_path (platform , clean_build = False ):
93
98
"""Get the folder that cmake configure and build in.
@@ -193,7 +198,7 @@ def get_ios_args(source_path):
193
198
else :
194
199
devices = SUPPORT_DEVICE
195
200
196
- global g_mobile_target_architectures
201
+ global g_target_architectures
197
202
# check architecture input
198
203
if (len (devices ) > 1 ):
199
204
archs_to_check = IOS_SUPPORT_ARCHITECTURE
@@ -205,14 +210,14 @@ def get_ios_args(source_path):
205
210
raise app .UsageError (
206
211
'Wrong architecture "{}" for device type {}, please pick from {}' .format (
207
212
arch , "," .join (devices ), "," .join (archs_to_check )))
208
- g_mobile_target_architectures = FLAGS .architecture
213
+ g_target_architectures = FLAGS .architecture
209
214
else :
210
- g_mobile_target_architectures = archs_to_check
215
+ g_target_architectures = archs_to_check
211
216
212
- if len (g_mobile_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
217
+ if len (g_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
213
218
# Need to override only if the archs are not default
214
219
result_args .append ("-DCMAKE_OSX_ARCHITECTURES=" +
215
- ";" .join (g_mobile_target_architectures ))
220
+ ";" .join (g_target_architectures ))
216
221
217
222
if len (devices ) != len (SUPPORT_DEVICE ):
218
223
# Need to override if only passed in device or simulator
@@ -254,56 +259,55 @@ def get_android_args():
254
259
'Neither ANDROID_NDK_HOME nor ANDROID_HOME is set.' )
255
260
256
261
# get architecture setup
257
- global g_mobile_target_architectures
262
+ global g_target_architectures
258
263
if FLAGS .architecture :
259
264
for arch in FLAGS .architecture :
260
265
if arch not in ANDROID_SUPPORT_ARCHITECTURE :
261
266
raise app .UsageError (
262
267
'Wrong architecture "{}", please pick from {}' .format (
263
268
arch , "," .join (ANDROID_SUPPORT_ARCHITECTURE )))
264
- g_mobile_target_architectures = FLAGS .architecture
269
+ g_target_architectures = FLAGS .architecture
265
270
else :
266
- g_mobile_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
271
+ g_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
267
272
268
- if len (g_mobile_target_architectures ) == 1 :
269
- result_args .append ("-DANDROID_ABI=" + g_mobile_target_architectures [0 ])
273
+ if len (g_target_architectures ) == 1 :
274
+ result_args .append ("-DANDROID_ABI=" + g_target_architectures [0 ])
270
275
271
276
result_args .append ("-DFIREBASE_ANDROID_BUILD=true" )
272
277
# android default to build release.
273
278
result_args .append ("-DCMAKE_BUILD_TYPE=release" )
274
279
result_args .append ("-DANDROID_STL=c++_shared" )
275
280
return result_args
276
281
277
-
278
282
def make_android_multi_arch_build (cmake_args , merge_script ):
279
- """Make android build for different architectures, and then combine them together
283
+ """Make android build for different architectures, and then combine them together.
284
+
280
285
Args:
281
286
cmake_args: cmake arguments used to build each architecture.
282
287
merge_script: script path to merge the srcaar files.
283
288
"""
284
- global g_mobile_target_architectures
285
- # build multiple archictures
289
+ global g_target_architectures
286
290
current_folder = os .getcwd ()
287
- for arch in g_mobile_target_architectures :
291
+ # build multiple archictures
292
+ for arch in g_target_architectures :
288
293
if not os .path .exists (arch ):
289
294
os .makedirs (arch )
290
- os .chdir ( arch )
295
+ build_dir = os .path . join ( current_folder , arch )
291
296
cmake_args .append ("-DANDROID_ABI=" + arch )
292
- subprocess .call (cmake_args )
293
- subprocess .call ("make" )
297
+ subprocess .call (cmake_args , cwd = build_dir )
298
+ subprocess .call ("make" , cwd = build_dir )
294
299
295
300
cmake_pack_args = [
296
- "cpack" ,
297
- "." ,
301
+ "cpack" ,
302
+ "." ,
298
303
]
299
- subprocess .call (cmake_pack_args )
300
- os .chdir (current_folder )
304
+ subprocess .call (cmake_pack_args , cwd = build_dir )
301
305
302
306
# merge them
303
307
zip_base_name = ""
304
308
srcarr_list = []
305
309
base_temp_dir = tempfile .mkdtemp ()
306
- for arch in g_mobile_target_architectures :
310
+ for arch in g_target_architectures :
307
311
# find *Android.zip in subfolder architecture
308
312
arch_zip_path = glob .glob (os .path .join (arch , "*Android.zip" ))
309
313
if not arch_zip_path :
@@ -350,19 +354,131 @@ def make_android_multi_arch_build(cmake_args, merge_script):
350
354
fullpath = os .path .join (current_root , filename )
351
355
zip_file .write (fullpath , os .path .relpath (fullpath , base_temp_dir ))
352
356
logging .info ("Generated Android multi-arch (%s) zip %s" ,
353
- "," .join (g_mobile_target_architectures ), final_zip_path )
357
+ "," .join (g_target_architectures ), final_zip_path )
354
358
355
359
def get_windows_args ():
356
360
"""Get the cmake args for windows platform specific.
357
361
358
362
Returns:
359
- camke args for windows platform.
363
+ cmake args for windows platform.
360
364
"""
361
365
result_args = []
362
366
result_args .append ('-G \" Visual Studio 16 2019\" ' )
363
367
result_args .append ('-A x64' ) # TODO flexibily for x32
364
368
result_args .append ("-DFIREBASE_PYTHON_HOST_EXECUTABLE:FILEPATH=%s" % sys .executable )
365
- return result_args
369
+ return result_args
370
+
371
+ def get_macos_args ():
372
+ """Get the cmake args for macos platform specific.
373
+
374
+ Returns:
375
+ cmake args for macos platform.
376
+ """
377
+ result_args = []
378
+ global g_target_architectures
379
+ # get architecture setup global g_target_architectures
380
+ if FLAGS .architecture :
381
+ for arch in FLAGS .architecture :
382
+ if arch not in MACOS_SUPPORT_ARCHITECTURE :
383
+ raise app .UsageError (
384
+ 'Wrong architecture "{}", please pick from {}' .format (
385
+ arch , "," .join (MACOS_SUPPORT_ARCHITECTURE )))
386
+ g_target_architectures = FLAGS .architecture
387
+ else :
388
+ # Default to selecting none, as it will likely only be able to build the local architecture.
389
+ g_target_architectures = []
390
+ if len (g_target_architectures ) == 1 :
391
+ result_args .append ('-DCMAKE_OSX_ARCHITECTURES=' + g_target_architectures [0 ])
392
+
393
+ return result_args
394
+
395
+ def make_macos_arch (arch , cmake_args ):
396
+ """Make the macos build for the given architecture.
397
+ Assumed to be called from the build directory.
398
+
399
+ Args:
400
+ arch: The architecture to build for.
401
+ cmake_args: Additional cmake arguments to use.
402
+ """
403
+ if not os .path .exists (arch ):
404
+ os .makedirs (arch )
405
+ build_dir = os .path .join (os .getcwd (), arch )
406
+ cmake_args .append ('-DCMAKE_OSX_ARCHITECTURES=' + arch )
407
+ subprocess .call (cmake_args , cwd = build_dir )
408
+ subprocess .call ('make' , cwd = build_dir )
409
+ subprocess .call (['cpack' , '.' ], cwd = build_dir )
410
+
411
+ def make_macos_multi_arch_build (cmake_args ):
412
+ """Make macos build for different architectures, and then combine them together
413
+
414
+ Args:
415
+ cmake_args: cmake arguments used to build each architecture.
416
+ """
417
+ global g_target_architectures
418
+ # build multiple architectures
419
+ current_folder = os .getcwd ()
420
+ threads = []
421
+ for arch in g_target_architectures :
422
+ t = threading .Thread (target = make_macos_arch , args = (arch , cmake_args ))
423
+ t .start ()
424
+ threads .append (t )
425
+
426
+ for t in threads :
427
+ t .join ()
428
+
429
+ # Merge the different zip files together, using lipo on the bundle files
430
+ zip_base_name = ""
431
+ bundle_list = []
432
+ base_temp_dir = tempfile .mkdtemp ()
433
+ for arch in g_target_architectures :
434
+ # find *Darwin.zip in subfolder architecture
435
+ arch_zip_path = glob .glob (os .path .join (arch , "*Darwin.zip" ))
436
+ if not arch_zip_path :
437
+ logging .error ("No *Darwin.zip generated for architecture %s" , arch )
438
+ return
439
+ if not zip_base_name :
440
+ # first architecture, so extract to the final temp folder. The following
441
+ # bundle files will merge to the ones in this folder.
442
+ zip_base_name = arch_zip_path [0 ]
443
+ with zipfile .ZipFile (zip_base_name ) as zip_file :
444
+ zip_file .extractall (base_temp_dir )
445
+ bundle_list .extend (glob .glob (os .path .join (
446
+ base_temp_dir , "**" , "*.bundle" ), recursive = True ))
447
+ else :
448
+ temporary_dir = tempfile .mkdtemp ()
449
+ # from the second *Darwin.zip, we only need to extract *.bundle files to operate the merge.
450
+ with zipfile .ZipFile (arch_zip_path [0 ]) as zip_file :
451
+ for file in zip_file .namelist ():
452
+ if file .endswith ('.bundle' ):
453
+ zip_file .extract (file , temporary_dir )
454
+ logging .debug ("Unpacked file %s from zip file %s to %s" ,
455
+ file , arch_zip_path , temporary_dir )
456
+
457
+ for bundle_file in bundle_list :
458
+ bundle_name = os .path .basename (bundle_file )
459
+ matching_files = glob .glob (os .path .join (
460
+ temporary_dir , "**" , "*" + bundle_name ), recursive = True )
461
+ if matching_files :
462
+ merge_args = [
463
+ "lipo" ,
464
+ bundle_file ,
465
+ matching_files [0 ],
466
+ "-create" ,
467
+ "-output" ,
468
+ bundle_file ,
469
+ ]
470
+ subprocess .call (merge_args )
471
+ logging .debug ("merging %s to %s" , matching_files [0 ], bundle_file )
472
+
473
+ # achive the temp folder to the final firebase_unity-<version>-Darwin.zip
474
+ final_zip_path = os .path .join (current_folder , os .path .basename (zip_base_name ))
475
+ with zipfile .ZipFile (final_zip_path , "w" , allowZip64 = True ) as zip_file :
476
+ for current_root , _ , filenames in os .walk (base_temp_dir ):
477
+ for filename in filenames :
478
+ fullpath = os .path .join (current_root , filename )
479
+ zip_file .write (fullpath , os .path .relpath (fullpath , base_temp_dir ))
480
+ logging .info ("Generated Darwin (MacOS) multi-arch (%s) zip %s" ,
481
+ "," .join (g_target_architectures ), final_zip_path )
366
482
367
483
def is_android_build ():
368
484
"""
@@ -386,6 +502,20 @@ def is_windows_build():
386
502
"""
387
503
return FLAGS .platform == "windows"
388
504
505
+ def is_macos_build ():
506
+ """
507
+ Returns:
508
+ If the build platform is macos
509
+ """
510
+ return FLAGS .platform == "macos"
511
+
512
+ def is_linux_build ():
513
+ """
514
+ Returns:
515
+ If the build platform is linux
516
+ """
517
+ return FLAGS .platform == "linux"
518
+
389
519
390
520
def main (argv ):
391
521
if len (argv ) > 1 :
@@ -401,9 +531,7 @@ def main(argv):
401
531
if is_android_build () and g_cpp_sdk_realpath :
402
532
# For android build, if we find local cpp folder,
403
533
# We trigger the cpp android build first.
404
- os .chdir (g_cpp_sdk_realpath )
405
- subprocess .call ("./gradlew" )
406
- os .chdir (source_path )
534
+ subprocess .call ("./gradlew" , cwd = g_cpp_sdk_realpath )
407
535
408
536
os .chdir (build_path )
409
537
cmake_setup_args = [
@@ -427,21 +555,30 @@ def main(argv):
427
555
if FLAGS .cmake_extras :
428
556
cmake_setup_args .extend (FLAGS .cmake_extras )
429
557
558
+ if FLAGS .use_boringssl :
559
+ cmake_setup_args .append ("-DFIREBASE_USE_BORINGSSL=ON" )
560
+
430
561
if is_ios_build ():
431
562
cmake_setup_args .extend (get_ios_args (source_path ))
432
563
elif is_android_build ():
433
564
cmake_setup_args .extend (get_android_args ())
434
565
elif is_windows_build ():
435
566
cmake_setup_args .extend (get_windows_args ())
567
+ elif is_macos_build ():
568
+ cmake_setup_args .extend (get_macos_args ())
436
569
437
- global g_mobile_target_architectures
570
+ global g_target_architectures
438
571
logging .info ("cmake_setup_args is: " + " " .join (cmake_setup_args ))
439
- if is_android_build () and len (g_mobile_target_architectures ) > 1 :
572
+ if is_android_build () and len (g_target_architectures ) > 1 :
440
573
logging .info ("Build android with multiple architectures %s" ,
441
- "," .join (g_mobile_target_architectures ))
574
+ "," .join (g_target_architectures ))
442
575
# android multi architecture build is a bit different
443
576
make_android_multi_arch_build (cmake_setup_args , os .path .join (
444
577
source_path , "aar_builder" , "merge_aar.py" ))
578
+ elif is_macos_build () and len (g_target_architectures ) > 1 :
579
+ logging .info ("Build macos with multiple architectures %s" ,
580
+ "," .join (g_target_architectures ))
581
+ make_macos_multi_arch_build (cmake_setup_args )
445
582
else :
446
583
subprocess .call (cmake_setup_args )
447
584
subprocess .call ("make" )
0 commit comments