2525import subprocess
2626import zipfile
2727import tempfile
28+ import threading
2829import sys
2930
3031from absl import app , flags , logging
5657
5758ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a" , "arm64-v8a" , "x86" , "x86_64" ]
5859
59- g_mobile_target_architectures = []
60+ MACOS_SUPPORT_ARCHITECTURE = ["x86_64" , "arm64" ]
61+
62+ g_target_architectures = []
6063g_cpp_sdk_realpath = ""
6164
6265FLAGS = flags .FLAGS
8083 "architecture" , None , "Which architectures in build on.\n "
8184 "For iOS device ({}).\n "
8285 "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" ]),
8488 "," .join (
8589 IOS_CONFIG_DICT ["simulator" ]["architecture" ]),
86- "," .join (ANDROID_SUPPORT_ARCHITECTURE )))
90+ "," .join (ANDROID_SUPPORT_ARCHITECTURE ),
91+ "," .join (MACOS_SUPPORT_ARCHITECTURE )))
8792flags .DEFINE_multi_string ('cmake_extras' , None ,
8893 "Any extra arguments wants to pass into cmake." )
8994flags .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." )
9196
9297def get_build_path (platform , clean_build = False ):
9398 """Get the folder that cmake configure and build in.
@@ -193,7 +198,7 @@ def get_ios_args(source_path):
193198 else :
194199 devices = SUPPORT_DEVICE
195200
196- global g_mobile_target_architectures
201+ global g_target_architectures
197202 # check architecture input
198203 if (len (devices ) > 1 ):
199204 archs_to_check = IOS_SUPPORT_ARCHITECTURE
@@ -205,14 +210,14 @@ def get_ios_args(source_path):
205210 raise app .UsageError (
206211 'Wrong architecture "{}" for device type {}, please pick from {}' .format (
207212 arch , "," .join (devices ), "," .join (archs_to_check )))
208- g_mobile_target_architectures = FLAGS .architecture
213+ g_target_architectures = FLAGS .architecture
209214 else :
210- g_mobile_target_architectures = archs_to_check
215+ g_target_architectures = archs_to_check
211216
212- if len (g_mobile_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
217+ if len (g_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
213218 # Need to override only if the archs are not default
214219 result_args .append ("-DCMAKE_OSX_ARCHITECTURES=" +
215- ";" .join (g_mobile_target_architectures ))
220+ ";" .join (g_target_architectures ))
216221
217222 if len (devices ) != len (SUPPORT_DEVICE ):
218223 # Need to override if only passed in device or simulator
@@ -254,56 +259,55 @@ def get_android_args():
254259 'Neither ANDROID_NDK_HOME nor ANDROID_HOME is set.' )
255260
256261 # get architecture setup
257- global g_mobile_target_architectures
262+ global g_target_architectures
258263 if FLAGS .architecture :
259264 for arch in FLAGS .architecture :
260265 if arch not in ANDROID_SUPPORT_ARCHITECTURE :
261266 raise app .UsageError (
262267 'Wrong architecture "{}", please pick from {}' .format (
263268 arch , "," .join (ANDROID_SUPPORT_ARCHITECTURE )))
264- g_mobile_target_architectures = FLAGS .architecture
269+ g_target_architectures = FLAGS .architecture
265270 else :
266- g_mobile_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
271+ g_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
267272
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 ])
270275
271276 result_args .append ("-DFIREBASE_ANDROID_BUILD=true" )
272277 # android default to build release.
273278 result_args .append ("-DCMAKE_BUILD_TYPE=release" )
274279 result_args .append ("-DANDROID_STL=c++_shared" )
275280 return result_args
276281
277-
278282def 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+
280285 Args:
281286 cmake_args: cmake arguments used to build each architecture.
282287 merge_script: script path to merge the srcaar files.
283288 """
284- global g_mobile_target_architectures
285- # build multiple archictures
289+ global g_target_architectures
286290 current_folder = os .getcwd ()
287- for arch in g_mobile_target_architectures :
291+ # build multiple archictures
292+ for arch in g_target_architectures :
288293 if not os .path .exists (arch ):
289294 os .makedirs (arch )
290- os .chdir ( arch )
295+ build_dir = os .path . join ( current_folder , arch )
291296 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 )
294299
295300 cmake_pack_args = [
296- "cpack" ,
297- "." ,
301+ "cpack" ,
302+ "." ,
298303 ]
299- subprocess .call (cmake_pack_args )
300- os .chdir (current_folder )
304+ subprocess .call (cmake_pack_args , cwd = build_dir )
301305
302306 # merge them
303307 zip_base_name = ""
304308 srcarr_list = []
305309 base_temp_dir = tempfile .mkdtemp ()
306- for arch in g_mobile_target_architectures :
310+ for arch in g_target_architectures :
307311 # find *Android.zip in subfolder architecture
308312 arch_zip_path = glob .glob (os .path .join (arch , "*Android.zip" ))
309313 if not arch_zip_path :
@@ -350,19 +354,131 @@ def make_android_multi_arch_build(cmake_args, merge_script):
350354 fullpath = os .path .join (current_root , filename )
351355 zip_file .write (fullpath , os .path .relpath (fullpath , base_temp_dir ))
352356 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 )
354358
355359def get_windows_args ():
356360 """Get the cmake args for windows platform specific.
357361
358362 Returns:
359- camke args for windows platform.
363+ cmake args for windows platform.
360364 """
361365 result_args = []
362366 result_args .append ('-G \" Visual Studio 16 2019\" ' )
363367 result_args .append ('-A x64' ) # TODO flexibily for x32
364368 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 )
366482
367483def is_android_build ():
368484 """
@@ -386,6 +502,20 @@ def is_windows_build():
386502 """
387503 return FLAGS .platform == "windows"
388504
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+
389519
390520def main (argv ):
391521 if len (argv ) > 1 :
@@ -401,9 +531,7 @@ def main(argv):
401531 if is_android_build () and g_cpp_sdk_realpath :
402532 # For android build, if we find local cpp folder,
403533 # 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 )
407535
408536 os .chdir (build_path )
409537 cmake_setup_args = [
@@ -427,21 +555,30 @@ def main(argv):
427555 if FLAGS .cmake_extras :
428556 cmake_setup_args .extend (FLAGS .cmake_extras )
429557
558+ if FLAGS .use_boringssl :
559+ cmake_setup_args .append ("-DFIREBASE_USE_BORINGSSL=ON" )
560+
430561 if is_ios_build ():
431562 cmake_setup_args .extend (get_ios_args (source_path ))
432563 elif is_android_build ():
433564 cmake_setup_args .extend (get_android_args ())
434565 elif is_windows_build ():
435566 cmake_setup_args .extend (get_windows_args ())
567+ elif is_macos_build ():
568+ cmake_setup_args .extend (get_macos_args ())
436569
437- global g_mobile_target_architectures
570+ global g_target_architectures
438571 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 :
440573 logging .info ("Build android with multiple architectures %s" ,
441- "," .join (g_mobile_target_architectures ))
574+ "," .join (g_target_architectures ))
442575 # android multi architecture build is a bit different
443576 make_android_multi_arch_build (cmake_setup_args , os .path .join (
444577 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 )
445582 else :
446583 subprocess .call (cmake_setup_args )
447584 subprocess .call ("make" )
0 commit comments