Skip to content

Commit fc9b605

Browse files
authored
Add support for Apple M1 Silicon (#281)
* Add logic to the python script to handle Mac arm64.
1 parent 78b195f commit fc9b605

File tree

3 files changed

+178
-37
lines changed

3 files changed

+178
-37
lines changed

.github/workflows/sdk_build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ jobs:
142142
if: startsWith(matrix.os, 'macos')
143143
shell: bash
144144
run: |
145-
./build_macos.sh ${{ github.event.inputs.additional_cmake_flags }}
145+
python scripts/build_scripts/build_zips.py --platform=macos --unity_root=$UNITY_ROOT_DIR --use_boringssl --architecture=x86_64 --architecture=arm64 --cmake_extras="${{ github.event.inputs.additional_cmake_flags }}"
146146
147147
- name: Build SDK (Windows)
148148
if: startsWith(matrix.os, 'windows')

docs/readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ Support
163163

164164
Release Notes
165165
-------------
166+
### Upcoming
167+
- Changes
168+
- General (Editor, macOS): Add support for Apple Silicon chips.
169+
166170
### 8.10.1
167171
- Changes
168172
- General (Android): Fix an issue when building with mainTemplate.gradle.

scripts/build_scripts/build_zips.py

Lines changed: 173 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import subprocess
2626
import zipfile
2727
import tempfile
28+
import threading
2829
import sys
2930

3031
from absl import app, flags, logging
@@ -56,7 +57,9 @@
5657

5758
ANDROID_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 = []
6063
g_cpp_sdk_realpath = ""
6164

6265
FLAGS = flags.FLAGS
@@ -80,14 +83,16 @@
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)))
8792
flags.DEFINE_multi_string('cmake_extras', None,
8893
"Any extra arguments wants to pass into cmake.")
8994
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.")
9196

9297
def 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-
278282
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+
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

355359
def 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

367483
def 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

390520
def 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

Comments
 (0)