Sign zips with apksigner

This commit is contained in:
topjohnwu 2020-12-26 16:04:41 -08:00
parent ab78a81d15
commit e9e6ad3bb0
1 changed files with 82 additions and 62 deletions

144
build.py
View File

@ -2,13 +2,17 @@
import sys import sys
import os import os
import subprocess import subprocess
import argparse
is_windows = os.name == 'nt' import multiprocessing
is_ci = 'CI' in os.environ and os.environ['CI'] == 'true' import zipfile
import datetime
if not is_ci and is_windows: import errno
import colorama import shutil
colorama.init() import lzma
import platform
import urllib.request
import os.path as op
from distutils.dir_util import copy_tree
def error(str): def error(str):
@ -31,6 +35,13 @@ def vprint(str):
print(str) print(str)
is_windows = os.name == 'nt'
is_ci = 'CI' in os.environ and os.environ['CI'] == 'true'
if not is_ci and is_windows:
import colorama
colorama.init()
# Environment checks # Environment checks
if not sys.version_info >= (3, 6): if not sys.version_info >= (3, 6):
error('Requires Python 3.6+') error('Requires Python 3.6+')
@ -44,19 +55,6 @@ try:
except FileNotFoundError: except FileNotFoundError:
error('Please install JDK and make sure \'javac\' is available in PATH') error('Please install JDK and make sure \'javac\' is available in PATH')
import argparse
import multiprocessing
import zipfile
import datetime
import errno
import shutil
import lzma
import tempfile
import platform
import urllib.request
import os.path as op
from distutils.dir_util import copy_tree
cpu_count = multiprocessing.cpu_count() cpu_count = multiprocessing.cpu_count()
archs = ['armeabi-v7a', 'x86'] archs = ['armeabi-v7a', 'x86']
arch64 = ['arm64-v8a', 'x86_64'] arch64 = ['arm64-v8a', 'x86_64']
@ -71,6 +69,8 @@ gradlew = op.join('.', 'gradlew' + ('.bat' if is_windows else ''))
# Global vars # Global vars
config = {} config = {}
STDOUT = None STDOUT = None
build_tools = None
def mv(source, target): def mv(source, target):
try: try:
@ -196,33 +196,52 @@ def clean_elf():
elf_cleaner = op.join('native', 'out', 'elf-cleaner') elf_cleaner = op.join('native', 'out', 'elf-cleaner')
if not op.exists(elf_cleaner): if not op.exists(elf_cleaner):
execv(['g++', '-std=c++11', 'tools/termux-elf-cleaner/termux-elf-cleaner.cpp', execv(['g++', '-std=c++11', 'tools/termux-elf-cleaner/termux-elf-cleaner.cpp',
'-o', elf_cleaner]) '-o', elf_cleaner])
args = [elf_cleaner] args = [elf_cleaner]
args.extend(op.join('native', 'out', arch, 'magisk') for arch in archs + arch64) args.extend(op.join('native', 'out', arch, 'magisk')
for arch in archs + arch64)
execv(args) execv(args)
def sign_zip(unsigned, output, release): def find_build_tools():
if not release or 'keyStore' not in config: global build_tools
mv(unsigned, output) if build_tools:
return build_tools
build_tools_root = op.join(os.environ['ANDROID_SDK_ROOT'], 'build-tools')
ls = os.listdir(build_tools_root)
# Use the latest build tools available
ls.sort()
build_tools = op.join(build_tools_root, ls[-1])
return build_tools
def sign_zip(unsigned):
if 'keyStore' not in config:
return return
signer_name = 'zipsigner-4.0.jar' msg = '* Signing APK'
zipsigner = op.join('app', 'signing', 'build', 'libs', signer_name) apksigner = op.join(find_build_tools(), 'apksigner')
if not op.exists(zipsigner): execArgs = [apksigner, 'sign',
header('* Building ' + signer_name) '--ks', config['keyStore'],
proc = execv([gradlew, 'app:signing:shadowJar']) '--ks-pass', f'pass:{config["keyStorePass"]}',
if proc.returncode != 0: '--ks-key-alias', config['keyAlias'],
error(f'Build {signer_name} failed!') '--key-pass', f'pass:{config["keyPass"]}',
'--v1-signer-name', 'CERT',
'--v4-signing-enabled', 'false']
header('* Signing Zip') if unsigned.endswith('.zip'):
msg = '* Signing zip'
execArgs.extend(['--min-sdk-version', '17',
'--v2-signing-enabled', 'false',
'--v3-signing-enabled', 'false'])
proc = execv(['java', '-jar', zipsigner, config['keyStore'], config['keyStorePass'], execArgs.append(unsigned)
config['keyAlias'], config['keyPass'], unsigned, output])
header(msg)
proc = execv(execArgs)
if proc.returncode != 0: if proc.returncode != 0:
error('Signing zip failed!') error('Signing failed!')
def binary_dump(src, out, var_name): def binary_dump(src, out, var_name):
@ -353,7 +372,7 @@ def build_apk(args, module):
build_type = 'Release' if args.release or module == 'stub' else 'Debug' build_type = 'Release' if args.release or module == 'stub' else 'Debug'
proc = execv([gradlew, f'{module}:assemble{build_type}', proc = execv([gradlew, f'{module}:assemble{build_type}',
'-PconfigPath=' + op.abspath(args.config)]) '-PconfigPath=' + op.abspath(args.config)])
if proc.returncode != 0: if proc.returncode != 0:
error(f'Build {module} failed!') error(f'Build {module} failed!')
@ -398,10 +417,16 @@ def build_snet(args):
def zip_main(args): def zip_main(args):
header('* Packing Flashable Zip') header('* Packing Flashable Zip')
with tempfile.NamedTemporaryFile(delete=False) as f: if config['prettyName']:
unsigned = f.name name = f'Magisk-v{config["version"]}.zip'
elif args.release:
name = 'magisk-release.zip'
else:
name = 'magisk-debug.zip'
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: output = op.join(config['outdir'], name)
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# update-binary # update-binary
target = op.join('META-INF', 'com', 'google', target = op.join('META-INF', 'com', 'google',
'android', 'update-binary') 'android', 'update-binary')
@ -454,20 +479,18 @@ def zip_main(args):
# End of zipping # End of zipping
output = op.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else sign_zip(output)
'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_zip(unsigned, output, args.release)
rm(unsigned)
header('Output: ' + output) header('Output: ' + output)
def zip_uninstaller(args): def zip_uninstaller(args):
header('* Packing Uninstaller Zip') header('* Packing Uninstaller Zip')
with tempfile.NamedTemporaryFile(delete=False) as f: datestr = datetime.datetime.now().strftime("%Y%m%d")
unsigned = f.name name = f'Magisk-uninstaller-{datestr}.zip' if config['prettyName'] else 'magisk-uninstaller.zip'
output = op.join(config['outdir'], name)
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# update-binary # update-binary
target = op.join('META-INF', 'com', 'google', target = op.join('META-INF', 'com', 'google',
'android', 'update-binary') 'android', 'update-binary')
@ -500,11 +523,7 @@ def zip_uninstaller(args):
# End of zipping # End of zipping
datestr = datetime.datetime.now().strftime("%Y%m%d") sign_zip(output)
output = op.join(config['outdir'], f'Magisk-uninstaller-{datestr}.zip'
if config['prettyName'] else 'magisk-uninstaller.zip')
sign_zip(unsigned, output, args.release)
rm(unsigned)
header('Output: ' + output) header('Output: ' + output)
@ -543,7 +562,7 @@ def setup_ndk(args):
for info in zf.infolist(): for info in zf.infolist():
extracted_path = zf.extract(info, ndk_root) extracted_path = zf.extract(info, ndk_root)
vprint(f'Extracting {info.filename}') vprint(f'Extracting {info.filename}')
if info.create_system == 3: # ZIP_UNIX_SYSTEM = 3 if info.create_system == 3: # ZIP_UNIX_SYSTEM = 3
unix_attributes = info.external_attr >> 16 unix_attributes = info.external_attr >> 16
if unix_attributes: if unix_attributes:
os.chmod(extracted_path, unix_attributes) os.chmod(extracted_path, unix_attributes)
@ -560,15 +579,15 @@ def setup_ndk(args):
header('* Replacing API-16 static libs') header('* Replacing API-16 static libs')
for target in ['arm-linux-androideabi', 'i686-linux-android']: for target in ['arm-linux-androideabi', 'i686-linux-android']:
arch = target.split('-')[0] arch = target.split('-')[0]
lib_dir = op.join( lib_dir = op.join(
ndk_path, 'toolchains', 'llvm', 'prebuilt', f'{os_name}-x86_64', ndk_path, 'toolchains', 'llvm', 'prebuilt', f'{os_name}-x86_64',
'sysroot', 'usr', 'lib', f'{target}', '16') 'sysroot', 'usr', 'lib', f'{target}', '16')
src_dir = op.join('tools', 'ndk-bins', arch) src_dir = op.join('tools', 'ndk-bins', arch)
# Remove stupid macOS crap # Remove stupid macOS crap
rm(op.join(src_dir, '.DS_Store')) rm(op.join(src_dir, '.DS_Store'))
for path in copy_tree(src_dir, lib_dir): for path in copy_tree(src_dir, lib_dir):
vprint(f'Replaced {path}') vprint(f'Replaced {path}')
def build_all(args): def build_all(args):
@ -581,6 +600,7 @@ def build_all(args):
parser = argparse.ArgumentParser(description='Magisk build script') parser = argparse.ArgumentParser(description='Magisk build script')
parser.set_defaults(func=lambda x: None)
parser.add_argument('-r', '--release', action='store_true', parser.add_argument('-r', '--release', action='store_true',
help='compile in release mode') help='compile in release mode')
parser.add_argument('-v', '--verbose', action='store_true', parser.add_argument('-v', '--verbose', action='store_true',