aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2020-03-10 16:48:51 -0700
committerDan Albert <danalbert@google.com>2020-03-10 17:09:04 -0700
commit094ac2ee3f8fb4fc312e3f02afac2cc6a8702650 (patch)
tree3fae1de075c95c2c97a60c5100be805200797af7
parent9116482600e8bffd4553ba1937898655708cfefb (diff)
downloadndk-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-xndk/checkbuild.py136
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)