diff options
author | Dan Albert <danalbert@google.com> | 2020-03-10 16:48:51 -0700 |
---|---|---|
committer | Dan Albert <danalbert@google.com> | 2020-03-10 17:09:04 -0700 |
commit | 094ac2ee3f8fb4fc312e3f02afac2cc6a8702650 (patch) | |
tree | 3fae1de075c95c2c97a60c5100be805200797af7 | |
parent | 9116482600e8bffd4553ba1937898655708cfefb (diff) | |
download | ndk-094ac2ee3f8fb4fc312e3f02afac2cc6a8702650.tar.gz |
Build a framework bundle for macOS.
Test: Removed the mac conditional part of this and the DMG packaging
to build on Linux. Output looks correct.
Bug: https://github.com/android/ndk/issues/1060
Change-Id: I55c36c89e145aa5f74e5b4c4d3e363539e1c7962
-rwxr-xr-x | ndk/checkbuild.py | 136 |
1 files changed, 116 insertions, 20 deletions
diff --git a/ndk/checkbuild.py b/ndk/checkbuild.py index 72f1a3f18..45b701035 100755 --- a/ndk/checkbuild.py +++ b/ndk/checkbuild.py @@ -85,6 +85,11 @@ import ndk.ui import ndk.workqueue +def get_version_string(build_number: str) -> str: + """Returns the version string for the current build.""" + return f'{ndk.config.major}.{ndk.config.hotfix}.{build_number}' + + def _make_tar_package(package_path: str, base_dir: str, path: str) -> str: """Creates a tarball package for distribution. @@ -125,6 +130,30 @@ def _make_zip_package(package_path: str, base_dir: str, path: str) -> str: os.chdir(cwd) +def make_dmg(dmg: Path, name: str, src: Path) -> None: + """Creates a macOS disk image. + + Args: + dmg: Output path of the created disk image. + name: Name of the volume. + src: Directory to package. + """ + subprocess.check_call([ + 'hdiutil', + 'create', + '-volname', + name, + '-srcfolder', + str(src), + # Overwrite existing + '-ov', + '-format', + # LZMA compressed + 'ULMO', + str(dmg) + ]) + + def purge_unwanted_files(ndk_dir: Path) -> None: """Removes unwanted files from the NDK install path.""" @@ -137,16 +166,91 @@ def purge_unwanted_files(ndk_dir: Path) -> None: file_path.unlink() -def package_ndk(ndk_dir: str, dist_dir: str, host_tag: str, +def create_plist(plist: Path, version: str) -> None: + """Populates the NDK plist at the given location.""" + + plist.write_text( + textwrap.dedent(f"""\ + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>CFBundleName</key> + <string>Android NDK</string> + <key>CFBundleDisplayName</key> + <string>Android NDK</string> + <key>CFBundleIdentifier</key> + <string>com.android.ndk</string> + <key>CFBundleVersion</key> + <string>{version}</string> + </dict> + </plist> + """)) + + +def make_framework_bundle(dmg: Path, ndk_dir: Path, build_number: str, + build_dir: Path) -> None: + """Builds a macOS Framework Bundle of the NDK. + + The NDK is distributed in two forms on macOS: as a framework bundle and as + a plain zip file. The zip file is needed by the SDK because AGP and Studio + expect the NDK to be contained one directory down in the archive, which is + not compatible with macOS bundles. The framework bundle is needed on macOS + because we rely on rpaths, and executables using rpaths are blocked by Gate + Keeper as of macOS Catalina (10.15), except for references within the same + bundle. + + Information on the macOS bundle format can be found at + https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html. + + Args: + dmg: The desired file path of the resultant disk image. + ndk_dir: The path to the NDK being bundled. + build_dir: The path to the top level build directory. + """ + framework_name = 'NDK' + bundle_dir = build_dir / f'bundle/AndroidNDK{build_number}.framework' + if bundle_dir.exists(): + shutil.rmtree(bundle_dir) + version_name = get_version_string(build_number) + versions_dir = bundle_dir / 'Versions' + + version_dir = versions_dir / version_name + version_dir.mkdir(parents=True) + bundled_ndk = version_dir / framework_name + shutil.copytree(ndk_dir, bundled_ndk) + + resources_dir = version_dir / 'Resources' + resources_dir.mkdir() + plist = resources_dir / 'Info.plist' + create_plist(plist, version_name) + + current_version = versions_dir / 'Current' + current_version.symlink_to(bundled_ndk.relative_to(version_dir), + target_is_directory=True) + + framework_link = bundle_dir / framework_name + framework_link.symlink_to(Path('Versions/Current') / framework_name, + target_is_directory=True) + + resources_link = bundle_dir / 'Resources' + resources_link.symlink_to('Versions/Current/Resources', + target_is_directory=True) + + make_dmg(dmg, f'Android NDK {ndk.config.release}', bundle_dir) + + +def package_ndk(ndk_dir: str, out_dir: str, dist_dir: str, host_tag: str, build_number: str) -> str: """Packages the built NDK for distribution. Args: ndk_dir (string): Path to the built NDK. + out_dir (string): Path to use for constructing any intermediate + outputs. dist_dir (string): Path to place the built package in. host_tag (string): Host tag to use in the package name, - build_number (printable): Build number to use in the package name. Will - be 'dev' if the argument evaluates to False. + build_number (string): Build number to use in the package name. """ package_name = 'android-ndk-{}-{}'.format(build_number, host_tag) package_path = os.path.join(dist_dir, package_name) @@ -155,6 +259,10 @@ def package_ndk(ndk_dir: str, dist_dir: str, host_tag: str, base_dir = os.path.dirname(ndk_dir) package_files = os.path.basename(ndk_dir) + if host_tag == 'darwin-x86_64': + bundle_path = Path(dist_dir) / f'AndroidNdk{build_number}.dmg' + make_framework_bundle(bundle_path, Path(ndk_dir), build_number, + Path(out_dir)) if host_tag.startswith('windows'): return _make_zip_package(package_path, base_dir, package_files) else: @@ -1671,11 +1779,8 @@ class Sysroot(ndk.builds.Module): beta = ndk.config.beta canary = '1' if ndk.config.canary else '0' assert self.context is not None - build = self.context.build_number - if build == 'dev': - build = '0' - ndk_version_h.write(textwrap.dedent("""\ + ndk_version_h.write(textwrap.dedent(f"""\ #pragma once /** @@ -1709,18 +1814,13 @@ class Sysroot(ndk.builds.Module): * * For a local development build of the NDK, this is -1. */ - #define __NDK_BUILD__ {build} + #define __NDK_BUILD__ {self.context.build_number} /** * Set to 1 if this is a canary build, 0 if not. */ #define __NDK_CANARY__ {canary} - """.format( - major=major, - minor=minor, - beta=beta, - build=build, - canary=canary))) + """)) build_support.make_package('sysroot', install_path, self.dist_dir) finally: @@ -2496,11 +2596,7 @@ class SourceProperties(ndk.builds.Module): path = self.get_install_path() with open(path, 'w') as source_properties: assert self.context is not None - build = self.context.build_number - if build == 'dev': - build = '0' - version = '{}.{}.{}'.format( - ndk.config.major, ndk.config.hotfix, build) + version = get_version_string(self.context.build_number) if ndk.config.beta > 0: version += '-beta{}'.format(ndk.config.beta) source_properties.writelines([ @@ -3002,7 +3098,7 @@ def main() -> None: # packaging, ensure that the directory is purged before and after # building the tests. package_path = package_ndk( - ndk_dir, dist_dir, host_tag, args.build_number) + ndk_dir, out_dir, dist_dir, host_tag, args.build_number) packaged_size_bytes = os.path.getsize(package_path) packaged_size = packaged_size_bytes // (2 ** 20) |