19
19
Example usage:
20
20
python build_zips.py --platform=macos --targets=auth --targets=firestore
21
21
"""
22
+ import glob
22
23
import os
23
- import re
24
- import subprocess
25
24
import shutil
25
+ import subprocess
26
+ import zipfile
27
+ import tempfile
26
28
27
- from absl import app
28
- from absl import flags
29
- from absl import logging
29
+ from absl import app , flags , logging
30
30
31
31
SUPPORT_PLATFORMS = ("linux" , "macos" , "windows" , "ios" , "android" )
32
32
SUPPORT_TARGETS = [
55
55
56
56
ANDROID_SUPPORT_ARCHITECTURE = ["armeabi-v7a" , "arm64-v8a" , "x86" , "x86_64" ]
57
57
58
+ g_mobile_target_architectures = []
59
+ g_cpp_sdk_realpath = ""
60
+
58
61
FLAGS = flags .FLAGS
59
62
flags .DEFINE_string (
60
63
'platform' , None ,
@@ -103,17 +106,22 @@ def get_build_path(platform, clean_build=False):
103
106
return platform_path
104
107
105
108
106
- def get_cpp_folder_args ():
109
+ def get_cpp_folder_args (source_path ):
107
110
"""Get the cmake args to pass in local Firebase C++ SDK folder.
108
111
If not found, will download from Firebase C++ git repo.
109
-
112
+
113
+ Args:
114
+ source_path: root source folder cd back.
115
+
110
116
Returns:
111
117
cmake args with the folder path of local Firebase C++ SDK.
112
118
Empty string if not found.
113
119
"""
120
+ global g_cpp_sdk_realpath
114
121
cpp_folder = os .path .join (os .getcwd (), ".." , "firebase-cpp-sdk" )
115
122
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
117
125
else :
118
126
return ""
119
127
@@ -184,6 +192,7 @@ def get_ios_args(source_path):
184
192
else :
185
193
devices = SUPPORT_DEVICE
186
194
195
+ global g_mobile_target_architectures
187
196
# check architecture input
188
197
if (len (devices ) > 1 ):
189
198
archs_to_check = IOS_SUPPORT_ARCHITECTURE
@@ -195,13 +204,14 @@ def get_ios_args(source_path):
195
204
raise app .UsageError (
196
205
'Wrong architecture "{}" for device type {}, please pick from {}' .format (
197
206
arch , "," .join (devices ), "," .join (archs_to_check )))
198
- archs = FLAGS .architecture
207
+ g_mobile_target_architectures = FLAGS .architecture
199
208
else :
200
- archs = archs_to_check
209
+ g_mobile_target_architectures = archs_to_check
201
210
202
- if len (archs ) != len (IOS_SUPPORT_ARCHITECTURE ):
211
+ if len (g_mobile_target_architectures ) != len (IOS_SUPPORT_ARCHITECTURE ):
203
212
# 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 ))
205
215
206
216
if len (devices ) != len (SUPPORT_DEVICE ):
207
217
# Need to override if only passed in device or simulator
@@ -214,6 +224,149 @@ def get_ios_args(source_path):
214
224
return result_args
215
225
216
226
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
+
217
370
def main (argv ):
218
371
if len (argv ) > 1 :
219
372
raise app .UsageError ('Too many command-line arguments.' )
@@ -222,15 +375,20 @@ def main(argv):
222
375
raise app .UsageError ('Wrong platform "{}", please pick from {}' .format (
223
376
platform , "," .join (SUPPORT_PLATFORMS )))
224
377
225
- cmake_cpp_folder_args = get_cpp_folder_args ()
226
- build_path = get_build_path (platform , FLAGS .clean_build )
227
-
228
378
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 )
229
387
230
388
os .chdir (build_path )
231
389
cmake_setup_args = [
232
390
"cmake" ,
233
- ".." ,
391
+ source_path ,
234
392
"-DFIREBASE_INCLUDE_UNITY=ON" ,
235
393
"-DFIREBASE_UNITY_BUILD_TESTS=ON" ,
236
394
"-DFIREBASE_CPP_BUILD_STUB_TESTS=ON" ,
@@ -249,19 +407,28 @@ def main(argv):
249
407
if FLAGS .cmake_extras :
250
408
cmake_setup_args .extend (FLAGS .cmake_extras )
251
409
252
- if platform == "ios" :
410
+ if is_ios_build () :
253
411
cmake_setup_args .extend (get_ios_args (source_path ))
412
+ elif is_android_build ():
413
+ cmake_setup_args .extend (get_android_args ())
254
414
415
+ global g_mobile_target_architectures
255
416
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 )
265
432
266
433
os .chdir (source_path )
267
434
0 commit comments