1919Example usage:
2020 python build_zips.py --platform=macos --targets=auth --targets=firestore
2121"""
22+ import glob
2223import os
23- import re
24- import subprocess
2524import shutil
25+ import subprocess
26+ import zipfile
27+ import tempfile
2628
27- from absl import app
28- from absl import flags
29- from absl import logging
29+ from absl import app , flags , logging
3030
3131SUPPORT_PLATFORMS = ("linux" , "macos" , "windows" , "ios" , "android" )
3232SUPPORT_TARGETS = [
5555
5656ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a" , "arm64-v8a" , "x86" , "x86_64" ]
5757
58+ g_mobile_target_architectures = []
59+ g_cpp_sdk_realpath = ""
60+
5861FLAGS = flags .FLAGS
5962flags .DEFINE_string (
6063 'platform' , None ,
@@ -103,17 +106,22 @@ def get_build_path(platform, clean_build=False):
103106 return platform_path
104107
105108
106- def get_cpp_folder_args ():
109+ def get_cpp_folder_args (source_path ):
107110 """Get the cmake args to pass in local Firebase C++ SDK folder.
108111 If not found, will download from Firebase C++ git repo.
109-
112+
113+ Args:
114+ source_path: root source folder cd back.
115+
110116 Returns:
111117 cmake args with the folder path of local Firebase C++ SDK.
112118 Empty string if not found.
113119 """
120+ global g_cpp_sdk_realpath
114121 cpp_folder = os .path .join (os .getcwd (), ".." , "firebase-cpp-sdk" )
115122 if os .path .exists (cpp_folder ):
116- return "-DFIREBASE_CPP_SDK_DIR=" + os .path .realpath (cpp_folder )
123+ g_cpp_sdk_realpath = os .path .realpath (cpp_folder )
124+ return "-DFIREBASE_CPP_SDK_DIR=" + g_cpp_sdk_realpath
117125 else :
118126 return ""
119127
@@ -184,6 +192,7 @@ def get_ios_args(source_path):
184192 else :
185193 devices = SUPPORT_DEVICE
186194
195+ global g_mobile_target_architectures
187196 # check architecture input
188197 if (len (devices ) > 1 ):
189198 archs_to_check = IOS_SUPPORT_ARCHITECTURE
@@ -195,13 +204,14 @@ def get_ios_args(source_path):
195204 raise app .UsageError (
196205 'Wrong architecture "{}" for device type {}, please pick from {}' .format (
197206 arch , "," .join (devices ), "," .join (archs_to_check )))
198- archs = FLAGS .architecture
207+ g_mobile_target_architectures = FLAGS .architecture
199208 else :
200- archs = archs_to_check
209+ g_mobile_target_architectures = archs_to_check
201210
202- if len (archs ) != len (IOS_SUPPORT_ARCHITECTURE ):
211+ if len (g_mobile_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
203212 # Need to override only if the archs are not default
204- result_args .append ("-DCMAKE_OSX_ARCHITECTURES=" + ";" .join (archs ))
213+ result_args .append ("-DCMAKE_OSX_ARCHITECTURES=" +
214+ ";" .join (g_mobile_target_architectures ))
205215
206216 if len (devices ) != len (SUPPORT_DEVICE ):
207217 # Need to override if only passed in device or simulator
@@ -214,6 +224,149 @@ def get_ios_args(source_path):
214224 return result_args
215225
216226
227+ def get_android_args ():
228+ """Get the cmake args for android platform specific.
229+
230+ Returns:
231+ camke args for android platform.
232+ """
233+ result_args = []
234+ # get Android NDK path
235+ system_android_ndk_home = os .getenv ('ANDROID_NDK_HOME' )
236+ if system_android_ndk_home :
237+ toolchain_path = os .path .join (
238+ system_android_ndk_home , "build" , "cmake" , "android.toolchain.cmake" )
239+ result_args .append ("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_path )
240+ logging .info ("Use ANDROID_NDK_HOME(%s) cmake toolchain(%s)" ,
241+ system_android_ndk_home , toolchain_path )
242+ else :
243+ system_android_home = os .getenv ('ANDROID_HOME' )
244+ if system_android_home :
245+ toolchain_files = glob .glob (os .path .join (system_android_home ,
246+ "**" , "build" , "cmake" , "android.toolchain.cmake" ), recursive = True )
247+ if toolchain_files :
248+ result_args .append ("-DCMAKE_TOOLCHAIN_FILE=" + toolchain_files [0 ])
249+ logging .info ("Use ANDROID_HOME(%s) cmake toolchain (%s)" ,
250+ system_android_home , toolchain_files [0 ])
251+ else :
252+ raise app .UsageError (
253+ 'Neither ANDROID_NDK_HOME nor ANDROID_HOME is set.' )
254+
255+ # get architecture setup
256+ global g_mobile_target_architectures
257+ if FLAGS .architecture :
258+ for arch in FLAGS .architecture :
259+ if arch not in ANDROID_SUPPORT_ARCHITECTURE :
260+ raise app .UsageError (
261+ 'Wrong architecture "{}", please pick from {}' .format (
262+ arch , "," .join (ANDROID_SUPPORT_ARCHITECTURE )))
263+ g_mobile_target_architectures = FLAGS .architecture
264+ else :
265+ g_mobile_target_architectures = ANDROID_SUPPORT_ARCHITECTURE
266+
267+ if len (g_mobile_target_architectures ) == 1 :
268+ result_args .append ("-DANDROID_ABI=" + g_mobile_target_architectures [0 ])
269+
270+ result_args .append ("-DFIREBASE_ANDROID_BUILD=true" )
271+ # android default to build release.
272+ result_args .append ("-DCMAKE_BUILD_TYPE=release" )
273+ return result_args
274+
275+
276+ def make_android_multi_arch_build (cmake_args , merge_script ):
277+ """Make android build for different architectures, and then combine them together
278+ Args:
279+ cmake_args: cmake arguments used to build each architecture.
280+ merge_script: script path to merge the srcaar files.
281+ """
282+ global g_mobile_target_architectures
283+ # build multiple archictures
284+ current_folder = os .getcwd ()
285+ for arch in g_mobile_target_architectures :
286+ if not os .path .exists (arch ):
287+ os .makedirs (arch )
288+ os .chdir (arch )
289+ cmake_args .append ("-DANDROID_ABI=" + arch )
290+ subprocess .call (cmake_args )
291+ subprocess .call ("make" )
292+
293+ cmake_pack_args = [
294+ "cpack" ,
295+ "." ,
296+ ]
297+ subprocess .call (cmake_pack_args )
298+ os .chdir (current_folder )
299+
300+ # merge them
301+ zip_base_name = ""
302+ srcarr_list = []
303+ base_temp_dir = tempfile .mkdtemp ()
304+ for arch in g_mobile_target_architectures :
305+ # find *Android.zip in subfolder architecture
306+ arch_zip_path = glob .glob (os .path .join (arch , "*Android.zip" ))
307+ if not arch_zip_path :
308+ logging .error ("No *Android.zip generated for architecture %s" , arch )
309+ return
310+ if not zip_base_name :
311+ # first architecture, so extract to the final temp folder. The following
312+ # srcaar files will merge to the ones in this folder.
313+ zip_base_name = arch_zip_path [0 ]
314+ with zipfile .ZipFile (zip_base_name ) as zip_file :
315+ zip_file .extractall (base_temp_dir )
316+ srcarr_list .extend (glob .glob (os .path .join (
317+ base_temp_dir , "**" , "*.srcaar" ), recursive = True ))
318+ else :
319+ temporary_dir = tempfile .mkdtemp ()
320+ # from the second *Android.zip, we only need to extract *.srcaar files to operate the merge.
321+ with zipfile .ZipFile (arch_zip_path [0 ]) as zip_file :
322+ for file in zip_file .namelist ():
323+ if file .endswith ('.srcaar' ):
324+ zip_file .extract (file , temporary_dir )
325+ logging .debug ("Unpacked file %s from zip file %s to %s" ,
326+ file , arch_zip_path , temporary_dir )
327+
328+ for srcaar_file in srcarr_list :
329+ srcaar_name = os .path .basename (srcaar_file )
330+ matching_files = glob .glob (os .path .join (
331+ temporary_dir , "**" , "*" + srcaar_name ), recursive = True )
332+ if matching_files :
333+ merge_args = [
334+ "python" ,
335+ merge_script ,
336+ "--inputs=" + srcaar_file ,
337+ "--inputs=" + matching_files [0 ],
338+ "--output=" + srcaar_file ,
339+ ]
340+ subprocess .call (merge_args )
341+ logging .debug ("merging %s to %s" , matching_files [0 ], srcaar_file )
342+
343+ # achive the temp folder to the final firebase_unity-<version>-Android.zip
344+ final_zip_path = os .path .join (current_folder , os .path .basename (zip_base_name ))
345+ with zipfile .ZipFile (final_zip_path , "w" , allowZip64 = True ) as zip_file :
346+ for current_root , _ , filenames in os .walk (base_temp_dir ):
347+ for filename in filenames :
348+ fullpath = os .path .join (current_root , filename )
349+ zip_file .write (fullpath , os .path .relpath (fullpath , base_temp_dir ))
350+ logging .info ("Generated Android multi-arch (%s) zip %s" ,
351+ "," .join (g_mobile_target_architectures ), final_zip_path )
352+
353+
354+ def is_android_build ():
355+ """
356+ Returns:
357+ If the build platform is android
358+ """
359+ return FLAGS .platform == "android"
360+
361+
362+ def is_ios_build ():
363+ """
364+ Returns:
365+ If the build platform is ios
366+ """
367+ return FLAGS .platform == "ios"
368+
369+
217370def main (argv ):
218371 if len (argv ) > 1 :
219372 raise app .UsageError ('Too many command-line arguments.' )
@@ -222,15 +375,20 @@ def main(argv):
222375 raise app .UsageError ('Wrong platform "{}", please pick from {}' .format (
223376 platform , "," .join (SUPPORT_PLATFORMS )))
224377
225- cmake_cpp_folder_args = get_cpp_folder_args ()
226- build_path = get_build_path (platform , FLAGS .clean_build )
227-
228378 source_path = os .getcwd ()
379+ cmake_cpp_folder_args = get_cpp_folder_args (source_path )
380+ build_path = get_build_path (platform , FLAGS .clean_build )
381+ if is_android_build () and g_cpp_sdk_realpath :
382+ # For android build, if we find local cpp folder,
383+ # We trigger the cpp android build first.
384+ os .chdir (g_cpp_sdk_realpath )
385+ subprocess .call ("./gradlew" )
386+ os .chdir (source_path )
229387
230388 os .chdir (build_path )
231389 cmake_setup_args = [
232390 "cmake" ,
233- ".." ,
391+ source_path ,
234392 "-DFIREBASE_INCLUDE_UNITY=ON" ,
235393 "-DFIREBASE_UNITY_BUILD_TESTS=ON" ,
236394 "-DFIREBASE_CPP_BUILD_STUB_TESTS=ON" ,
@@ -249,19 +407,28 @@ def main(argv):
249407 if FLAGS .cmake_extras :
250408 cmake_setup_args .extend (FLAGS .cmake_extras )
251409
252- if platform == "ios" :
410+ if is_ios_build () :
253411 cmake_setup_args .extend (get_ios_args (source_path ))
412+ elif is_android_build ():
413+ cmake_setup_args .extend (get_android_args ())
254414
415+ global g_mobile_target_architectures
255416 logging .info ("cmake_setup_args is: " + " " .join (cmake_setup_args ))
256-
257- subprocess .call (cmake_setup_args )
258- subprocess .call ("make" )
259-
260- cmake_pack_args = [
261- "cpack" ,
262- "." ,
263- ]
264- subprocess .call (cmake_pack_args )
417+ if is_android_build () and len (g_mobile_target_architectures ) > 1 :
418+ logging .info ("Build android with multiple architectures %s" ,
419+ "," .join (g_mobile_target_architectures ))
420+ # android multi architecture build is a bit different
421+ make_android_multi_arch_build (cmake_setup_args , os .path .join (
422+ source_path , "aar_builder" , "merge_aar.py" ))
423+ else :
424+ subprocess .call (cmake_setup_args )
425+ subprocess .call ("make" )
426+
427+ cmake_pack_args = [
428+ "cpack" ,
429+ "." ,
430+ ]
431+ subprocess .call (cmake_pack_args )
265432
266433 os .chdir (source_path )
267434
0 commit comments