Misc Formatting

* PEP8 and linting
* empty exceptions
This commit is contained in:
Aidan Holland 2019-02-11 03:18:15 -05:00 committed by John Wu
parent 49640ce03a
commit 65ebb0d2f8
2 changed files with 371 additions and 319 deletions

View File

@ -1,41 +1,44 @@
# Magisk # Magisk
[Downloads](https://github.com/topjohnwu/Magisk/releases) | [Documentation](https://topjohnwu.github.io/Magisk/) | [XDA Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
[Downloads](https://github.com/topjohnwu/Magisk/releases) \| [Documentation](https://topjohnwu.github.io/Magisk/) \| [XDA Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
## Introduction ## Introduction
Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 4.2 (API 17). It covers the fundamental parts for Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc. Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 4.2 (API 17). It covers the fundamental parts for Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc.
Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any system integrity verifications used in banking apps, corporation monitoring apps, game cheat detections, and most importantly [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html). Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any system integrity verifications used in banking apps, corporation monitoring apps, game cheat detections, and most importantly [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).
## Bug Reports ## Bug Reports
**Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** **DO NOT** report bugs that is already fixed upstream. Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread. **Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** **DO NOT** report bugs that is already fixed upstream. Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread.
## Building Environment Requirements ## Building Environment Requirements
1. Python 3: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips 1. Python 3: run `build.py` script
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK 2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or optionally specify a custom path `ANDROID_NDK_HOME` 3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes 4. Android NDK: Install NDK along with SDK (`$ANDROID_HOME/ndk-bundle`), or optionally specify a custom path `ANDROID_NDK_HOME`
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
## Building Notes and Instructions ## Building Notes and Instructions
1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
2. Building is supported on macOS, Linux, and Windows. Official releases are built and tested with [FrankeNDK](https://github.com/topjohnwu/FrankeNDK); point `ANDROID_NDK_HOME` to FrankeNDK if you want to use it for compiling. 1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example. 2. Building is supported on macOS, Linux, and Windows. Official releases are built and tested with [FrankeNDK](https://github.com/topjohnwu/FrankeNDK); point `ANDROID_NDK_HOME` to FrankeNDK if you want to use it for compiling.
4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h` 3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually). 4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## License ## License
``` Magisk, including all git submodules are free software:
Magisk, including all git submodules are free software: you can redistribute it and/or modify it under the terms of the
you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,
GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
```

641
build.py
View File

@ -4,31 +4,36 @@ import os
import subprocess import subprocess
if os.name == 'nt': if os.name == 'nt':
import colorama import colorama
colorama.init() colorama.init()
def error(str): def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n') print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1) sys.exit(1)
def header(str): def header(str):
print('\n' + '\033[44m' + str + '\033[0m' + '\n') print('\n' + '\033[44m' + str + '\033[0m' + '\n')
def vprint(str): def vprint(str):
if args.verbose: if args.verbose:
print(str) print(str)
# 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+')
if 'ANDROID_HOME' not in os.environ: if 'ANDROID_HOME' not in os.environ:
error('Please add Android SDK path to ANDROID_HOME environment variable!') error('Please add Android SDK path to ANDROID_HOME environment variable!')
try: try:
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) subprocess.run(['java', '-version'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except FileNotFoundError: except FileNotFoundError:
error('Please install JDK and make sure \'java\' is available in PATH') error('Please install JDK and make sure \'java\' is available in PATH')
import argparse import argparse
import multiprocessing import multiprocessing
@ -37,13 +42,13 @@ import datetime
import errno import errno
import shutil import shutil
import lzma import lzma
import base64
import tempfile import tempfile
if 'ANDROID_NDK_HOME' in os.environ: if 'ANDROID_NDK_HOME' in os.environ:
ndk_build = os.path.join(os.environ['ANDROID_NDK_HOME'], 'ndk-build') ndk_build = os.path.join(os.environ['ANDROID_NDK_HOME'], 'ndk-build')
else: else:
ndk_build = os.path.join(os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build') ndk_build = os.path.join(
os.environ['ANDROID_HOME'], 'ndk-bundle', 'ndk-build')
cpu_count = multiprocessing.cpu_count() cpu_count = multiprocessing.cpu_count()
gradlew = os.path.join('.', 'gradlew.bat' if os.name == 'nt' else 'gradlew') gradlew = os.path.join('.', 'gradlew.bat' if os.name == 'nt' else 'gradlew')
@ -51,380 +56,424 @@ archs = ['armeabi-v7a', 'x86']
keystore = 'release-key.jks' keystore = 'release-key.jks'
config = {} config = {}
def mv(source, target): def mv(source, target):
try: try:
shutil.move(source, target) shutil.move(source, target)
except: except shutil.Error:
pass pass
def cp(source, target): def cp(source, target):
try: try:
shutil.copyfile(source, target) shutil.copyfile(source, target)
vprint(f'cp: {source} -> {target}') vprint(f'cp: {source} -> {target}')
except: except shutil.Error:
pass pass
def rm(file): def rm(file):
try: try:
os.remove(file) os.remove(file)
except OSError as e: except OSError as e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
def mkdir(path, mode=0o777): def mkdir(path, mode=0o777):
try: try:
os.mkdir(path, mode) os.mkdir(path, mode)
except: except OSError:
pass pass
def mkdir_p(path, mode=0o777): def mkdir_p(path, mode=0o777):
os.makedirs(path, mode, exist_ok=True) os.makedirs(path, mode, exist_ok=True)
def zip_with_msg(zip_file, source, target):
if not os.path.exists(source):
error(f'{source} does not exist! Try build \'binary\' and \'apk\' before zipping!')
zip_file.write(source, target)
vprint(f'zip: {source} -> {target}')
def zip_with_msg(zipfile, source, target):
if not os.path.exists(source):
error(f'{source} does not exist! Try build \'binary\' and \'apk\' before zipping!')
zipfile.write(source, target)
vprint(f'zip: {source} -> {target}')
def collect_binary(): def collect_binary():
for arch in archs: for arch in archs:
mkdir_p(os.path.join('native', 'out', arch)) mkdir_p(os.path.join('native', 'out', arch))
for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox']: for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox']:
source = os.path.join('native', 'libs', arch, bin) source = os.path.join('native', 'libs', arch, bin)
target = os.path.join('native', 'out', arch, bin) target = os.path.join('native', 'out', arch, bin)
mv(source, target) mv(source, target)
def execv(cmd, redirect=None): def execv(cmd, redirect=None):
return subprocess.run(cmd, stdout=redirect if redirect != None else STDOUT) return subprocess.run(cmd, stdout=redirect if redirect != None else STDOUT)
def system(cmd, redirect=None): def system(cmd, redirect=None):
return subprocess.run(cmd, shell=True, stdout=redirect if redirect != None else STDOUT) return subprocess.run(cmd, shell=True, stdout=redirect if redirect != None else STDOUT)
def xz(data): def xz(data):
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE) return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
def sign_zip(unsigned, output, release): def sign_zip(unsigned, output, release):
signer_name = 'zipsigner-3.0.jar' signer_name = 'zipsigner-3.0.jar'
zipsigner = os.path.join('signing', 'build', 'libs', signer_name) zipsigner = os.path.join('signing', 'build', 'libs', signer_name)
if not os.path.exists(zipsigner): if not os.path.exists(zipsigner):
header('* Building ' + signer_name) header('* Building ' + signer_name)
proc = execv([gradlew, 'signing:shadowJar']) proc = execv([gradlew, 'signing:shadowJar'])
if proc.returncode != 0: if proc.returncode != 0:
error(f'Build {signer_name} failed!') error(f'Build {signer_name} failed!')
header('* Signing Zip') header('* Signing Zip')
if release: if release:
proc = execv(['java', '-jar', zipsigner, keystore, config['keyStorePass'], config['keyAlias'], config['keyPass'], unsigned, output]) proc = execv(['java', '-jar', zipsigner, keystore, config['keyStorePass'],
else: config['keyAlias'], config['keyPass'], unsigned, output])
proc = execv(['java', '-jar', zipsigner, unsigned, output]) else:
proc = execv(['java', '-jar', zipsigner, unsigned, output])
if proc.returncode != 0:
error('Signing zip failed!')
if proc.returncode != 0:
error('Signing zip failed!')
def binary_dump(src, out, var_name): def binary_dump(src, out, var_name):
out.write(f'const static unsigned char {var_name}[] = {{') out.write(f'const static unsigned char {var_name}[] = {{')
for i, c in enumerate(xz(src.read())): for i, c in enumerate(xz(src.read())):
if i % 16 == 0: if i % 16 == 0:
out.write('\n') out.write('\n')
out.write(f'0x{c:02X},') out.write(f'0x{c:02X},')
out.write('\n};\n') out.write('\n};\n')
out.flush() out.flush()
def gen_update_binary(): def gen_update_binary():
bs = 1024 bs = 1024
update_bin = bytearray(bs) update_bin = bytearray(bs)
file = os.path.join('native', 'out', 'x86', 'busybox') file = os.path.join('native', 'out', 'x86', 'busybox')
with open(file, 'rb') as f: with open(file, 'rb') as f:
x86_bb = f.read() x86_bb = f.read()
file = os.path.join('native', 'out', 'armeabi-v7a', 'busybox') file = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
with open(file, 'rb') as f: with open(file, 'rb') as f:
arm_bb = f.read() arm_bb = f.read()
file = os.path.join('scripts', 'update_binary.sh') file = os.path.join('scripts', 'update_binary.sh')
with open(file, 'rb') as f: with open(file, 'rb') as f:
script = f.read() script = f.read()
# Align x86 busybox to bs # Align x86 busybox to bs
blkCnt = (len(x86_bb) - 1) // bs + 1 blkCnt = (len(x86_bb) - 1) // bs + 1
script = script.replace(b'__X86_CNT__', b'%d' % blkCnt) script = script.replace(b'__X86_CNT__', b'%d' % blkCnt)
update_bin[:len(script)] = script update_bin[:len(script)] = script
update_bin.extend(x86_bb) update_bin.extend(x86_bb)
# Padding for alignment # Padding for alignment
update_bin.extend(b'\0' * (blkCnt * bs - len(x86_bb))) update_bin.extend(b'\0' * (blkCnt * bs - len(x86_bb)))
update_bin.extend(arm_bb) update_bin.extend(arm_bb)
return update_bin return update_bin
def build_binary(args): def build_binary(args):
support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox'} support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox'}
if len(args.target) == 0: if args.target:
# If nothing specified, build everything args.target = set(args.target) & support_targets
args.target = support_targets else:
else: # If nothing specified, build everything
args.target = set(args.target) & support_targets args.target = support_targets
if len(args.target) == 0: # Unsure why this is separate
return if not args.target:
return
header('* Building binaries: ' + ' '.join(args.target)) header('* Building binaries: ' + ' '.join(args.target))
os.utime(os.path.join('native', 'jni', 'include', 'flags.h')) os.utime(os.path.join('native', 'jni', 'include', 'flags.h'))
# Basic flags # Basic flags
base_flags = f'MAGISK_VERSION="{config["version"]}" MAGISK_VER_CODE={config["versionCode"]}' base_flags = f'MAGISK_VERSION="{config["version"]}" MAGISK_VER_CODE={config["versionCode"]}'
if not args.release: if not args.release:
base_flags += ' MAGISK_DEBUG=1' base_flags += ' MAGISK_DEBUG=1'
# Magisk is special case as it is a dependency of magiskinit # Magisk is special case as it is a dependency of magiskinit
if 'magisk' in args.target: if 'magisk' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_MAGISK=1 -j{cpu_count}') proc = system(f'{ndk_build} -C native {base_flags} B_MAGISK=1 -j{cpu_count}')
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk binary failed!') error('Build Magisk binary failed!')
collect_binary() collect_binary()
# Dump the binary to header # Dump the binary to header
for arch in archs: for arch in archs:
bin_file = os.path.join('native', 'out', arch, 'magisk') bin_file = os.path.join('native', 'out', arch, 'magisk')
with open(os.path.join('native', 'out', arch, 'binaries_arch.h'), 'w') as out: with open(os.path.join('native', 'out', arch, 'binaries_arch.h'), 'w') as out:
with open(bin_file, 'rb') as src: with open(bin_file, 'rb') as src:
binary_dump(src, out, 'magisk_xz') binary_dump(src, out, 'magisk_xz')
# BusyBox is special case as it needs special flags to build # BusyBox is special case as it needs special flags to build
if 'busybox' in args.target: if 'busybox' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_BB=1 -j{cpu_count}') proc = system(f'{ndk_build} -C native {base_flags} B_BB=1 -j{cpu_count}')
if proc.returncode != 0: if proc.returncode != 0:
error('Build binaries failed!') error('Build binaries failed!')
collect_binary() collect_binary()
build = False build = False
flags = base_flags flags = base_flags
if 'magiskinit' in args.target: if 'magiskinit' in args.target:
if not os.path.exists(os.path.join('native', 'out', 'x86', 'binaries_arch.h')): if not os.path.exists(os.path.join('native', 'out', 'x86', 'binaries_arch.h')):
error('Build "magisk" before building "magiskinit"') error('Build "magisk" before building "magiskinit"')
if not os.path.exists(os.path.join('native', 'out', 'binaries.h')): if not os.path.exists(os.path.join('native', 'out', 'binaries.h')):
error('Build stub APK before building "magiskinit"') error('Build stub APK before building "magiskinit"')
flags += ' B_INIT=1' flags += ' B_INIT=1'
build = True build = True
if 'magiskboot' in args.target: if 'magiskboot' in args.target:
flags += ' B_BOOT=1' flags += ' B_BOOT=1'
build = True build = True
if build:
proc = system(f'{ndk_build} -C native {flags} -j{cpu_count}')
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
if build:
proc = system(f'{ndk_build} -C native {flags} -j{cpu_count}')
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
def build_apk(args, flavor): def build_apk(args, flavor):
header('* Building {} Magisk Manager'.format(flavor)) header('* Building {} Magisk Manager'.format(flavor))
buildType = 'Release' if args.release else 'Debug' build_type = 'Release' if args.release else 'Debug'
proc = execv([gradlew, f'app:assemble{flavor}{buildType}', '-PconfigPath=' + os.path.abspath(args.config)]) proc = execv([gradlew, f'app:assemble{flavor}{build_type}', '-PconfigPath=' + os.path.abspath(args.config)])
if proc.returncode != 0: if proc.returncode != 0:
error('Build Magisk Manager failed!') error('Build Magisk Manager failed!')
flavor = flavor.lower() flavor = flavor.lower()
buildType = buildType.lower() build_type = build_type.lower()
apk = f'app-{flavor}-{buildType}.apk' apk = f'app-{flavor}-{build_type}.apk'
source = os.path.join('app', 'build', 'outputs',
'apk', flavor, build_type, apk)
target = os.path.join(config['outdir'], apk)
mv(source, target)
header('Output: ' + target)
return target
source = os.path.join('app', 'build', 'outputs', 'apk', flavor, buildType, apk)
target = os.path.join(config['outdir'], apk)
mv(source, target)
header('Output: ' + target)
return target
def build_app(args): def build_app(args):
source = os.path.join('scripts', 'util_functions.sh') source = os.path.join('scripts', 'util_functions.sh')
target = os.path.join('app-core', 'src', 'main', 'res', 'raw', 'util_functions.sh') target = os.path.join('app-core', 'src', 'main',
cp(source, target) 'res', 'raw', 'util_functions.sh')
build_apk(args, 'Full') cp(source, target)
build_apk(args, 'Full')
def build_stub(args): def build_stub(args):
stub = build_apk(args, 'Stub') stub = build_apk(args, 'Stub')
# Dump the stub APK to header # Dump the stub APK to header
mkdir(os.path.join('native', 'out')) mkdir(os.path.join('native', 'out'))
with open(os.path.join('native', 'out', 'binaries.h'), 'w') as out: with open(os.path.join('native', 'out', 'binaries.h'), 'w') as out:
with open(stub, 'rb') as src: with open(stub, 'rb') as src:
binary_dump(src, out, 'manager_xz'); binary_dump(src, out, 'manager_xz')
def build_snet(args): def build_snet(args):
proc = execv([gradlew, 'snet:assembleRelease']) proc = execv([gradlew, 'snet:assembleRelease'])
if proc.returncode != 0: if proc.returncode != 0:
error('Build snet extention failed!') error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk') source = os.path.join('snet', 'build', 'outputs', 'apk',
target = os.path.join(config['outdir'], 'snet.apk') 'release', 'snet-release-unsigned.apk')
# Re-compress the whole APK for smaller size target = os.path.join(config['outdir'], 'snet.apk')
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout: # Re-compress the whole APK for smaller size
with zipfile.ZipFile(source) as zin: with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
for item in zin.infolist(): with zipfile.ZipFile(source) as zin:
zout.writestr(item.filename, zin.read(item)) for item in zin.infolist():
rm(source) zout.writestr(item.filename, zin.read(item))
header('Output: ' + target) rm(source)
header('Output: ' + target)
def zip_main(args): def zip_main(args):
header('* Packing Flashable Zip') header('* Packing Flashable Zip')
unsigned = tempfile.mkstemp()[1] unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF # META-INF
# update-binary # update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary') target = os.path.join('META-INF', 'com', 'google',
vprint('zip: ' + target) 'android', 'update-binary')
zipf.writestr(target, gen_update_binary()) vprint('zip: ' + target)
# updater-script zipf.writestr(target, gen_update_binary())
source = os.path.join('scripts', 'flash_script.sh') # updater-script
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script') source = os.path.join('scripts', 'flash_script.sh')
zip_with_msg(zipf, source, target) target = os.path.join('META-INF', 'com', 'google',
'android', 'updater-script')
zip_with_msg(zipf, source, target)
# Binaries # Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]: for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for binary in ['magiskinit', 'magiskboot']: for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, binary) source = os.path.join('native', 'out', lib_dir, binary)
target = os.path.join(zip_dir, binary) target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# APK # APK
source = os.path.join(config['outdir'], 'app-full-release.apk' if args.release else 'app-full-debug.apk') source = os.path.join(
target = os.path.join('common', 'magisk.apk') config['outdir'], 'app-full-release.apk' if args.release else 'app-full-debug.apk')
zip_with_msg(zipf, source, target) target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target)
# Scripts # Scripts
# boot_patch.sh # boot_patch.sh
source = os.path.join('scripts', 'boot_patch.sh') source = os.path.join('scripts', 'boot_patch.sh')
target = os.path.join('common', 'boot_patch.sh') target = os.path.join('common', 'boot_patch.sh')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# util_functions.sh # util_functions.sh
source = os.path.join('scripts', 'util_functions.sh') source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script: with open(source, 'r') as script:
# Add version info util_functions.sh # Add version info util_functions.sh
util_func = script.read().replace('#MAGISK_VERSION_STUB', util_func = script.read().replace('#MAGISK_VERSION_STUB',
f'MAGISK_VER="{config["version"]}"\nMAGISK_VER_CODE={config["versionCode"]}') f'MAGISK_VER="{config["version"]}"\nMAGISK_VER_CODE={config["versionCode"]}')
target = os.path.join('common', 'util_functions.sh') target = os.path.join('common', 'util_functions.sh')
vprint(f'zip: {source} -> {target}') vprint(f'zip: {source} -> {target}')
zipf.writestr(target, util_func) zipf.writestr(target, util_func)
# addon.d.sh # addon.d.sh
source = os.path.join('scripts', 'addon.d.sh') source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('common', 'addon.d.sh') target = os.path.join('common', 'addon.d.sh')
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# Prebuilts # Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']: for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos) source = os.path.join('chromeos', chromeos)
zip_with_msg(zipf, source, source) zip_with_msg(zipf, source, source)
# End of zipping # End of zipping
output = os.path.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else
'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_zip(unsigned, output, args.release)
header('Output: ' + output)
output = os.path.join(config['outdir'], f'Magisk-v{config["version"]}.zip' if config['prettyName'] else
'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_zip(unsigned, output, args.release)
header('Output: ' + output)
def zip_uninstaller(args): def zip_uninstaller(args):
header('* Packing Uninstaller Zip') header('* Packing Uninstaller Zip')
unsigned = tempfile.mkstemp()[1] unsigned = tempfile.mkstemp()[1]
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf: with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF # META-INF
# update-binary # update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary') target = os.path.join('META-INF', 'com', 'google',
vprint('zip: ' + target) 'android', 'update-binary')
zipf.writestr(target, gen_update_binary()) vprint('zip: ' + target)
# updater-script zipf.writestr(target, gen_update_binary())
source = os.path.join('scripts', 'magisk_uninstaller.sh') # updater-script
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script') source = os.path.join('scripts', 'magisk_uninstaller.sh')
zip_with_msg(zipf, source, target) target = os.path.join('META-INF', 'com', 'google',
'android', 'updater-script')
zip_with_msg(zipf, source, target)
# Binaries # Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]: for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for bin in ['magisk', 'magiskboot']: for bin in ['magisk', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, bin) source = os.path.join('native', 'out', lib_dir, bin)
target = os.path.join(zip_dir, bin) target = os.path.join(zip_dir, bin)
zip_with_msg(zipf, source, target) zip_with_msg(zipf, source, target)
# Scripts # Scripts
# util_functions.sh # util_functions.sh
source = os.path.join('scripts', 'util_functions.sh') source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script: with open(source, 'r') as script:
target = os.path.join('util_functions.sh') target = os.path.join('util_functions.sh')
vprint(f'zip: {source} -> {target}') vprint(f'zip: {source} -> {target}')
zipf.writestr(target, script.read()) zipf.writestr(target, script.read())
# Prebuilts # Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']: for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos) source = os.path.join('chromeos', chromeos)
zip_with_msg(zipf, source, source) zip_with_msg(zipf, source, source)
# End of zipping # End of zipping
output = os.path.join(config['outdir'], f'Magisk-uninstaller-{datetime.datetime.now().strftime("%Y%m%d")}.zip'
if config['prettyName'] else 'magisk-uninstaller.zip')
sign_zip(unsigned, output, args.release)
header('Output: ' + output)
output = os.path.join(config['outdir'], f'Magisk-uninstaller-{datetime.datetime.now().strftime("%Y%m%d")}.zip'
if config['prettyName'] else 'magisk-uninstaller.zip')
sign_zip(unsigned, output, args.release)
header('Output: ' + output)
def cleanup(args): def cleanup(args):
support_targets = {'native', 'java'} support_targets = {'native', 'java'}
if len(args.target) == 0: if args.target:
# If nothing specified, clean everything args.target = set(args.target) & support_targets
args.target = support_targets else:
else: # If nothing specified, clean everything
args.target = set(args.target) & support_targets args.target = support_targets
if 'native' in args.target: if 'native' in args.target:
header('* Cleaning native') header('* Cleaning native')
system(ndk_build + ' -C native B_MAGISK=1 B_INIT=1 B_BOOT=1 B_BB=1 clean') system(ndk_build + ' -C native B_MAGISK=1 B_INIT=1 B_BOOT=1 B_BB=1 clean')
shutil.rmtree(os.path.join('native', 'out'), ignore_errors=True) shutil.rmtree(os.path.join('native', 'out'), ignore_errors=True)
if 'java' in args.target:
header('* Cleaning java')
execv([gradlew, 'app:clean', 'app-core:clean',
'snet:clean', 'signing:clean'])
if 'java' in args.target:
header('* Cleaning java')
execv([gradlew, 'app:clean', 'app-core:clean', 'snet:clean', 'signing:clean'])
def build_all(args): def build_all(args):
vars(args)['target'] = [] vars(args)['target'] = []
build_stub(args) build_stub(args)
build_app(args) build_app(args)
build_binary(args) build_binary(args)
zip_main(args) zip_main(args)
zip_uninstaller(args) zip_uninstaller(args)
build_snet(args) build_snet(args)
parser = argparse.ArgumentParser(description='Magisk build script') parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('-r', '--release', action='store_true', help='compile Magisk for release') parser.add_argument('-r', '--release', action='store_true',
parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') help='compile Magisk for release')
parser.add_argument('-c', '--config', default='config.prop', help='config file location') parser.add_argument('-v', '--verbose', action='store_true',
help='verbose output')
parser.add_argument('-c', '--config', default='config.prop',
help='config file location')
subparsers = parser.add_subparsers(title='actions') subparsers = parser.add_subparsers(title='actions')
all_parser = subparsers.add_parser('all', help='build everything (binaries/apks/zips)') all_parser = subparsers.add_parser(
'all', help='build everything (binaries/apks/zips)')
all_parser.set_defaults(func=build_all) all_parser.set_defaults(func=build_all)
binary_parser = subparsers.add_parser('binary', help='build binaries') binary_parser = subparsers.add_parser('binary', help='build binaries')
binary_parser.add_argument('target', nargs='*', help='Support: magisk, magiskinit, magiskboot, busybox. Leave empty to build all.') binary_parser.add_argument(
'target', nargs='*', help='Support: magisk, magiskinit, magiskboot, busybox. Leave empty to build all.')
binary_parser.set_defaults(func=build_binary) binary_parser.set_defaults(func=build_binary)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK') apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
apk_parser.set_defaults(func=build_app) apk_parser.set_defaults(func=build_app)
stub_parser = subparsers.add_parser('stub', help='build stub Magisk Manager APK') stub_parser = subparsers.add_parser(
'stub', help='build stub Magisk Manager APK')
stub_parser.set_defaults(func=build_stub) stub_parser.set_defaults(func=build_stub)
snet_parser = subparsers.add_parser('snet', help='build snet extention for Magisk Manager') snet_parser = subparsers.add_parser(
'snet', help='build snet extention for Magisk Manager')
snet_parser.set_defaults(func=build_snet) snet_parser.set_defaults(func=build_snet)
zip_parser = subparsers.add_parser('zip', help='zip Magisk into a flashable zip') zip_parser = subparsers.add_parser(
'zip', help='zip Magisk into a flashable zip')
zip_parser.set_defaults(func=zip_main) zip_parser.set_defaults(func=zip_main)
un_parser = subparsers.add_parser('uninstaller', help='create flashable uninstaller') un_parser = subparsers.add_parser(
'uninstaller', help='create flashable uninstaller')
un_parser.set_defaults(func=zip_uninstaller) un_parser.set_defaults(func=zip_uninstaller)
clean_parser = subparsers.add_parser('clean', help='cleanup.') clean_parser = subparsers.add_parser('clean', help='cleanup.')
clean_parser.add_argument('target', nargs='*', help='Support: native, java. Leave empty to clean all.') clean_parser.add_argument(
'target', nargs='*', help='Support: native, java. Leave empty to clean all.')
clean_parser.set_defaults(func=cleanup) clean_parser.set_defaults(func=cleanup)
if len(sys.argv) == 1: if len(sys.argv) == 1:
parser.print_help() parser.print_help()
sys.exit(1) sys.exit(1)
args = parser.parse_args() args = parser.parse_args()
@ -433,25 +482,25 @@ config['outdir'] = 'out'
config['prettyName'] = 'false' config['prettyName'] = 'false'
with open(args.config, 'r') as f: with open(args.config, 'r') as f:
for line in [l.strip(' \t\r\n') for l in f]: for line in [l.strip(' \t\r\n') for l in f]:
if line.startswith('#') or len(line) == 0: if line.startswith('#') or len(line) == 0:
continue continue
prop = line.split('=') prop = line.split('=')
config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n') config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
if 'version' not in config or 'versionCode' not in config: if 'version' not in config or 'versionCode' not in config:
error('"version" and "versionCode" is required in "config.prop"') error('"version" and "versionCode" is required in "config.prop"')
try: try:
config['versionCode'] = int(config['versionCode']) config['versionCode'] = int(config['versionCode'])
except ValueError: except ValueError:
error('"versionCode" is required to be an integer') error('"versionCode" is required to be an integer')
config['prettyName'] = config['prettyName'].lower() == 'true' config['prettyName'] = config['prettyName'].lower() == 'true'
mkdir_p(config['outdir']) mkdir_p(config['outdir'])
if args.release and not os.path.exists(keystore): if args.release and not os.path.exists(keystore):
error(f'Please generate a java keystore and place it in "{keystore}"') error(f'Please generate a java keystore and place it in "{keystore}"')
STDOUT = None if args.verbose else subprocess.DEVNULL STDOUT = None if args.verbose else subprocess.DEVNULL
args.func(args) args.func(args)