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
[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
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).
## 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.
## Building Environment Requirements
1. Python 3: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
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
1. Python 3: run `build.py` script
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
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
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.
3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
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).
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.
3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
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
```
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
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,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```
You should have received a copy of the GNU General Public License
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
if os.name == 'nt':
import colorama
colorama.init()
import colorama
colorama.init()
def error(str):
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1)
print('\n' + '\033[41m' + str + '\033[0m' + '\n')
sys.exit(1)
def header(str):
print('\n' + '\033[44m' + str + '\033[0m' + '\n')
print('\n' + '\033[44m' + str + '\033[0m' + '\n')
def vprint(str):
if args.verbose:
print(str)
if args.verbose:
print(str)
# Environment checks
if not sys.version_info >= (3, 6):
error('Requires Python 3.6+')
error('Requires Python 3.6+')
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:
subprocess.run(['java', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(['java', '-version'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
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 multiprocessing
@ -37,13 +42,13 @@ import datetime
import errno
import shutil
import lzma
import base64
import tempfile
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:
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()
gradlew = os.path.join('.', 'gradlew.bat' if os.name == 'nt' else 'gradlew')
@ -51,380 +56,424 @@ archs = ['armeabi-v7a', 'x86']
keystore = 'release-key.jks'
config = {}
def mv(source, target):
try:
shutil.move(source, target)
except:
pass
try:
shutil.move(source, target)
except shutil.Error:
pass
def cp(source, target):
try:
shutil.copyfile(source, target)
vprint(f'cp: {source} -> {target}')
except:
pass
try:
shutil.copyfile(source, target)
vprint(f'cp: {source} -> {target}')
except shutil.Error:
pass
def rm(file):
try:
os.remove(file)
except OSError as e:
if e.errno != errno.ENOENT:
raise
try:
os.remove(file)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def mkdir(path, mode=0o777):
try:
os.mkdir(path, mode)
except:
pass
try:
os.mkdir(path, mode)
except OSError:
pass
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():
for arch in archs:
mkdir_p(os.path.join('native', 'out', arch))
for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox']:
source = os.path.join('native', 'libs', arch, bin)
target = os.path.join('native', 'out', arch, bin)
mv(source, target)
for arch in archs:
mkdir_p(os.path.join('native', 'out', arch))
for bin in ['magisk', 'magiskinit', 'magiskboot', 'busybox']:
source = os.path.join('native', 'libs', arch, bin)
target = os.path.join('native', 'out', arch, bin)
mv(source, target)
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):
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):
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):
signer_name = 'zipsigner-3.0.jar'
zipsigner = os.path.join('signing', 'build', 'libs', signer_name)
signer_name = 'zipsigner-3.0.jar'
zipsigner = os.path.join('signing', 'build', 'libs', signer_name)
if not os.path.exists(zipsigner):
header('* Building ' + signer_name)
proc = execv([gradlew, 'signing:shadowJar'])
if proc.returncode != 0:
error(f'Build {signer_name} failed!')
if not os.path.exists(zipsigner):
header('* Building ' + signer_name)
proc = execv([gradlew, 'signing:shadowJar'])
if proc.returncode != 0:
error(f'Build {signer_name} failed!')
header('* Signing Zip')
header('* Signing Zip')
if release:
proc = execv(['java', '-jar', zipsigner, keystore, config['keyStorePass'], config['keyAlias'], config['keyPass'], unsigned, output])
else:
proc = execv(['java', '-jar', zipsigner, unsigned, output])
if release:
proc = execv(['java', '-jar', zipsigner, keystore, config['keyStorePass'],
config['keyAlias'], config['keyPass'], 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):
out.write(f'const static unsigned char {var_name}[] = {{')
for i, c in enumerate(xz(src.read())):
if i % 16 == 0:
out.write('\n')
out.write(f'0x{c:02X},')
out.write('\n};\n')
out.flush()
out.write(f'const static unsigned char {var_name}[] = {{')
for i, c in enumerate(xz(src.read())):
if i % 16 == 0:
out.write('\n')
out.write(f'0x{c:02X},')
out.write('\n};\n')
out.flush()
def gen_update_binary():
bs = 1024
update_bin = bytearray(bs)
file = os.path.join('native', 'out', 'x86', 'busybox')
with open(file, 'rb') as f:
x86_bb = f.read()
file = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
with open(file, 'rb') as f:
arm_bb = f.read()
file = os.path.join('scripts', 'update_binary.sh')
with open(file, 'rb') as f:
script = f.read()
# Align x86 busybox to bs
blkCnt = (len(x86_bb) - 1) // bs + 1
script = script.replace(b'__X86_CNT__', b'%d' % blkCnt)
update_bin[:len(script)] = script
update_bin.extend(x86_bb)
# Padding for alignment
update_bin.extend(b'\0' * (blkCnt * bs - len(x86_bb)))
update_bin.extend(arm_bb)
return update_bin
bs = 1024
update_bin = bytearray(bs)
file = os.path.join('native', 'out', 'x86', 'busybox')
with open(file, 'rb') as f:
x86_bb = f.read()
file = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
with open(file, 'rb') as f:
arm_bb = f.read()
file = os.path.join('scripts', 'update_binary.sh')
with open(file, 'rb') as f:
script = f.read()
# Align x86 busybox to bs
blkCnt = (len(x86_bb) - 1) // bs + 1
script = script.replace(b'__X86_CNT__', b'%d' % blkCnt)
update_bin[:len(script)] = script
update_bin.extend(x86_bb)
# Padding for alignment
update_bin.extend(b'\0' * (blkCnt * bs - len(x86_bb)))
update_bin.extend(arm_bb)
return update_bin
def build_binary(args):
support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox'}
if len(args.target) == 0:
# If nothing specified, build everything
args.target = support_targets
else:
args.target = set(args.target) & support_targets
support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox'}
if args.target:
args.target = set(args.target) & support_targets
else:
# If nothing specified, build everything
args.target = support_targets
if len(args.target) == 0:
return
# Unsure why this is separate
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
base_flags = f'MAGISK_VERSION="{config["version"]}" MAGISK_VER_CODE={config["versionCode"]}'
if not args.release:
base_flags += ' MAGISK_DEBUG=1'
# Basic flags
base_flags = f'MAGISK_VERSION="{config["version"]}" MAGISK_VER_CODE={config["versionCode"]}'
if not args.release:
base_flags += ' MAGISK_DEBUG=1'
# Magisk is special case as it is a dependency of magiskinit
if 'magisk' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_MAGISK=1 -j{cpu_count}')
if proc.returncode != 0:
error('Build Magisk binary failed!')
collect_binary()
# Dump the binary to header
for arch in archs:
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(bin_file, 'rb') as src:
binary_dump(src, out, 'magisk_xz')
# Magisk is special case as it is a dependency of magiskinit
if 'magisk' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_MAGISK=1 -j{cpu_count}')
if proc.returncode != 0:
error('Build Magisk binary failed!')
collect_binary()
# Dump the binary to header
for arch in archs:
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(bin_file, 'rb') as src:
binary_dump(src, out, 'magisk_xz')
# BusyBox is special case as it needs special flags to build
if 'busybox' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_BB=1 -j{cpu_count}')
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
# BusyBox is special case as it needs special flags to build
if 'busybox' in args.target:
proc = system(f'{ndk_build} -C native {base_flags} B_BB=1 -j{cpu_count}')
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
build = False
flags = base_flags
build = False
flags = base_flags
if 'magiskinit' in args.target:
if not os.path.exists(os.path.join('native', 'out', 'x86', 'binaries_arch.h')):
error('Build "magisk" before building "magiskinit"')
if not os.path.exists(os.path.join('native', 'out', 'binaries.h')):
error('Build stub APK before building "magiskinit"')
flags += ' B_INIT=1'
build = True
if 'magiskinit' in args.target:
if not os.path.exists(os.path.join('native', 'out', 'x86', 'binaries_arch.h')):
error('Build "magisk" before building "magiskinit"')
if not os.path.exists(os.path.join('native', 'out', 'binaries.h')):
error('Build stub APK before building "magiskinit"')
flags += ' B_INIT=1'
build = True
if 'magiskboot' in args.target:
flags += ' B_BOOT=1'
build = True
if 'magiskboot' in args.target:
flags += ' B_BOOT=1'
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):
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)])
if proc.returncode != 0:
error('Build Magisk Manager failed!')
proc = execv([gradlew, f'app:assemble{flavor}{build_type}', '-PconfigPath=' + os.path.abspath(args.config)])
if proc.returncode != 0:
error('Build Magisk Manager failed!')
flavor = flavor.lower()
buildType = buildType.lower()
apk = f'app-{flavor}-{buildType}.apk'
flavor = flavor.lower()
build_type = build_type.lower()
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):
source = os.path.join('scripts', 'util_functions.sh')
target = os.path.join('app-core', 'src', 'main', 'res', 'raw', 'util_functions.sh')
cp(source, target)
build_apk(args, 'Full')
source = os.path.join('scripts', 'util_functions.sh')
target = os.path.join('app-core', 'src', 'main',
'res', 'raw', 'util_functions.sh')
cp(source, target)
build_apk(args, 'Full')
def build_stub(args):
stub = build_apk(args, 'Stub')
# Dump the stub APK to header
mkdir(os.path.join('native', 'out'))
with open(os.path.join('native', 'out', 'binaries.h'), 'w') as out:
with open(stub, 'rb') as src:
binary_dump(src, out, 'manager_xz');
stub = build_apk(args, 'Stub')
# Dump the stub APK to header
mkdir(os.path.join('native', 'out'))
with open(os.path.join('native', 'out', 'binaries.h'), 'w') as out:
with open(stub, 'rb') as src:
binary_dump(src, out, 'manager_xz')
def build_snet(args):
proc = execv([gradlew, 'snet:assembleRelease'])
if proc.returncode != 0:
error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
target = os.path.join(config['outdir'], 'snet.apk')
# Re-compress the whole APK for smaller size
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
with zipfile.ZipFile(source) as zin:
for item in zin.infolist():
zout.writestr(item.filename, zin.read(item))
rm(source)
header('Output: ' + target)
proc = execv([gradlew, 'snet:assembleRelease'])
if proc.returncode != 0:
error('Build snet extention failed!')
source = os.path.join('snet', 'build', 'outputs', 'apk',
'release', 'snet-release-unsigned.apk')
target = os.path.join(config['outdir'], 'snet.apk')
# Re-compress the whole APK for smaller size
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
with zipfile.ZipFile(source) as zin:
for item in zin.infolist():
zout.writestr(item.filename, zin.read(item))
rm(source)
header('Output: ' + target)
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:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
vprint('zip: ' + target)
zipf.writestr(target, gen_update_binary())
# updater-script
source = os.path.join('scripts', 'flash_script.sh')
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
zip_with_msg(zipf, source, target)
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google',
'android', 'update-binary')
vprint('zip: ' + target)
zipf.writestr(target, gen_update_binary())
# updater-script
source = os.path.join('scripts', 'flash_script.sh')
target = os.path.join('META-INF', 'com', 'google',
'android', 'updater-script')
zip_with_msg(zipf, source, target)
# Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, binary)
target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target)
# Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for binary in ['magiskinit', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, binary)
target = os.path.join(zip_dir, binary)
zip_with_msg(zipf, source, target)
# APK
source = os.path.join(config['outdir'], 'app-full-release.apk' if args.release else 'app-full-debug.apk')
target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target)
# APK
source = os.path.join(
config['outdir'], 'app-full-release.apk' if args.release else 'app-full-debug.apk')
target = os.path.join('common', 'magisk.apk')
zip_with_msg(zipf, source, target)
# Scripts
# boot_patch.sh
source = os.path.join('scripts', 'boot_patch.sh')
target = os.path.join('common', 'boot_patch.sh')
zip_with_msg(zipf, source, target)
# util_functions.sh
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
# Add version info util_functions.sh
util_func = script.read().replace('#MAGISK_VERSION_STUB',
f'MAGISK_VER="{config["version"]}"\nMAGISK_VER_CODE={config["versionCode"]}')
target = os.path.join('common', 'util_functions.sh')
vprint(f'zip: {source} -> {target}')
zipf.writestr(target, util_func)
# addon.d.sh
source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('common', 'addon.d.sh')
zip_with_msg(zipf, source, target)
# Scripts
# boot_patch.sh
source = os.path.join('scripts', 'boot_patch.sh')
target = os.path.join('common', 'boot_patch.sh')
zip_with_msg(zipf, source, target)
# util_functions.sh
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
# Add version info util_functions.sh
util_func = script.read().replace('#MAGISK_VERSION_STUB',
f'MAGISK_VER="{config["version"]}"\nMAGISK_VER_CODE={config["versionCode"]}')
target = os.path.join('common', 'util_functions.sh')
vprint(f'zip: {source} -> {target}')
zipf.writestr(target, util_func)
# addon.d.sh
source = os.path.join('scripts', 'addon.d.sh')
target = os.path.join('common', 'addon.d.sh')
zip_with_msg(zipf, source, target)
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos)
zip_with_msg(zipf, source, source)
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos)
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):
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:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google', 'android', 'update-binary')
vprint('zip: ' + target)
zipf.writestr(target, gen_update_binary())
# updater-script
source = os.path.join('scripts', 'magisk_uninstaller.sh')
target = os.path.join('META-INF', 'com', 'google', 'android', 'updater-script')
zip_with_msg(zipf, source, target)
with zipfile.ZipFile(unsigned, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zipf:
# META-INF
# update-binary
target = os.path.join('META-INF', 'com', 'google',
'android', 'update-binary')
vprint('zip: ' + target)
zipf.writestr(target, gen_update_binary())
# updater-script
source = os.path.join('scripts', 'magisk_uninstaller.sh')
target = os.path.join('META-INF', 'com', 'google',
'android', 'updater-script')
zip_with_msg(zipf, source, target)
# Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for bin in ['magisk', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, bin)
target = os.path.join(zip_dir, bin)
zip_with_msg(zipf, source, target)
# Binaries
for lib_dir, zip_dir in [('armeabi-v7a', 'arm'), ('x86', 'x86')]:
for bin in ['magisk', 'magiskboot']:
source = os.path.join('native', 'out', lib_dir, bin)
target = os.path.join(zip_dir, bin)
zip_with_msg(zipf, source, target)
# Scripts
# util_functions.sh
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
target = os.path.join('util_functions.sh')
vprint(f'zip: {source} -> {target}')
zipf.writestr(target, script.read())
# Scripts
# util_functions.sh
source = os.path.join('scripts', 'util_functions.sh')
with open(source, 'r') as script:
target = os.path.join('util_functions.sh')
vprint(f'zip: {source} -> {target}')
zipf.writestr(target, script.read())
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos)
zip_with_msg(zipf, source, source)
# Prebuilts
for chromeos in ['futility', 'kernel_data_key.vbprivk', 'kernel.keyblock']:
source = os.path.join('chromeos', chromeos)
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):
support_targets = {'native', 'java'}
if len(args.target) == 0:
# If nothing specified, clean everything
args.target = support_targets
else:
args.target = set(args.target) & support_targets
support_targets = {'native', 'java'}
if args.target:
args.target = set(args.target) & support_targets
else:
# If nothing specified, clean everything
args.target = support_targets
if 'native' in args.target:
header('* Cleaning native')
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)
if 'native' in args.target:
header('* Cleaning native')
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)
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):
vars(args)['target'] = []
build_stub(args)
build_app(args)
build_binary(args)
zip_main(args)
zip_uninstaller(args)
build_snet(args)
vars(args)['target'] = []
build_stub(args)
build_app(args)
build_binary(args)
zip_main(args)
zip_uninstaller(args)
build_snet(args)
parser = argparse.ArgumentParser(description='Magisk build script')
parser.add_argument('-r', '--release', action='store_true', help='compile Magisk for release')
parser.add_argument('-v', '--verbose', action='store_true', help='verbose output')
parser.add_argument('-c', '--config', default='config.prop', help='config file location')
parser.add_argument('-r', '--release', action='store_true',
help='compile Magisk for release')
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')
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)
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)
apk_parser = subparsers.add_parser('apk', help='build Magisk Manager APK')
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)
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)
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)
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)
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)
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
parser.print_help()
sys.exit(1)
args = parser.parse_args()
@ -433,25 +482,25 @@ config['outdir'] = 'out'
config['prettyName'] = 'false'
with open(args.config, 'r') as f:
for line in [l.strip(' \t\r\n') for l in f]:
if line.startswith('#') or len(line) == 0:
continue
prop = line.split('=')
config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
for line in [l.strip(' \t\r\n') for l in f]:
if line.startswith('#') or len(line) == 0:
continue
prop = line.split('=')
config[prop[0].strip(' \t\r\n')] = prop[1].strip(' \t\r\n')
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:
config['versionCode'] = int(config['versionCode'])
config['versionCode'] = int(config['versionCode'])
except ValueError:
error('"versionCode" is required to be an integer')
error('"versionCode" is required to be an integer')
config['prettyName'] = config['prettyName'].lower() == 'true'
mkdir_p(config['outdir'])
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
args.func(args)