Update scripts

1. Update build.py to use f-strings
2. Directly append busybox binaries to update-binary
3. Remove b64xz
This commit is contained in:
topjohnwu 2019-01-15 08:32:18 -05:00
parent 93ee0c8798
commit 23e5188422
4 changed files with 72 additions and 167 deletions

109
build.py
View File

@ -60,7 +60,7 @@ def mv(source, target):
def cp(source, target): def cp(source, target):
try: try:
shutil.copyfile(source, target) shutil.copyfile(source, target)
vprint('cp: {} -> {}'.format(source, target)) vprint(f'cp: {source} -> {target}')
except: except:
pass pass
@ -82,9 +82,9 @@ def mkdir_p(path, mode=0o777):
def zip_with_msg(zipfile, source, target): def zip_with_msg(zipfile, source, target):
if not os.path.exists(source): if not os.path.exists(source):
error('{} does not exist! Try build \'binary\' and \'apk\' before zipping!'.format(source)) error(f'{source} does not exist! Try build \'binary\' and \'apk\' before zipping!')
zipfile.write(source, target) zipfile.write(source, target)
vprint('zip: {} -> {}'.format(source, target)) vprint(f'zip: {source} -> {target}')
def collect_binary(): def collect_binary():
for arch in archs: for arch in archs:
@ -111,7 +111,7 @@ def sign_zip(unsigned, output, release):
header('* Building ' + signer_name) header('* Building ' + signer_name)
proc = execv([gradlew, 'utils:shadowJar']) proc = execv([gradlew, 'utils:shadowJar'])
if proc.returncode != 0: if proc.returncode != 0:
error('Build {} failed!'.format(signer_name)) error(f'Build {signer_name} failed!')
header('* Signing Zip') header('* Signing Zip')
@ -124,41 +124,38 @@ def sign_zip(unsigned, output, release):
error('Signing zip failed!') error('Signing zip failed!')
def binary_dump(src, out, var_name): def binary_dump(src, out, var_name):
out.write('const static unsigned char {}[] = {{'.format(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('0x{:02X},'.format(c)) 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():
update_bin = [] bs = 1024
binary = os.path.join('native', 'out', 'armeabi-v7a', 'b64xz') update_bin = bytearray(bs)
if not os.path.exists(binary): file = os.path.join('native', 'out', 'x86', 'busybox')
error('Please build \'binary\' before zipping!') with open(file, 'rb') as f:
with open(binary, 'rb') as b64xz: x86_bb = f.read()
update_bin.append('#! /sbin/sh\nEX_ARM=\'') file = os.path.join('native', 'out', 'armeabi-v7a', 'busybox')
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read())) with open(file, 'rb') as f:
binary = os.path.join('native', 'out', 'x86', 'b64xz') arm_bb = f.read()
with open(binary, 'rb') as b64xz: file = os.path.join('scripts', 'update_binary.sh')
update_bin.append('\'\nEX_X86=\'') with open(file, 'rb') as f:
update_bin.append(''.join("\\x{:02X}".format(c) for c in b64xz.read())) script = f.read()
binary = os.path.join('native', 'out', 'armeabi-v7a', 'busybox') # Align x86 busybox to bs
with open(binary, 'rb') as busybox: blkCnt = (len(x86_bb) - 1) // bs + 1
update_bin.append('\'\nBB_ARM=') script = script.replace(b'__X86_CNT__', b'%d' % blkCnt)
update_bin.append(base64.b64encode(xz(busybox.read())).decode('ascii')) update_bin[:len(script)] = script
binary = os.path.join('native', 'out', 'x86', 'busybox') update_bin.extend(x86_bb)
with open(binary, 'rb') as busybox: # Padding for alignment
update_bin.append('\nBB_X86=') update_bin.extend(b'\0' * (blkCnt * bs - len(x86_bb)))
update_bin.append(base64.b64encode(xz(busybox.read())).decode('ascii')) update_bin.extend(arm_bb)
update_bin.append('\n') return update_bin
with open(os.path.join('scripts', 'update_binary.sh'), 'r') as script:
update_bin.append(script.read())
return ''.join(update_bin)
def build_binary(args): def build_binary(args):
support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox', 'b64xz'} support_targets = {'magisk', 'magiskinit', 'magiskboot', 'busybox'}
if len(args.target) == 0: if len(args.target) == 0:
# If nothing specified, build everything # If nothing specified, build everything
args.target = support_targets args.target = support_targets
@ -173,13 +170,13 @@ def build_binary(args):
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 = 'MAGISK_VERSION=\"{}\" MAGISK_VER_CODE={}'.format(config['version'], 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'
if 'magisk' in args.target:
# Magisk is special case as it is a dependency of magiskinit # Magisk is special case as it is a dependency of magiskinit
proc = system('{} -C native {} B_MAGISK=1 -j{}'.format(ndk_build, base_flags, cpu_count)) if 'magisk' in args.target:
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()
@ -190,12 +187,15 @@ def build_binary(args):
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')
old_plat = False # BusyBox is special case as it needs special flags to build
flags = base_flags 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()
if 'b64xz' in args.target: build = False
flags += ' B_BXZ=1' flags = base_flags
old_plat = True
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')):
@ -203,20 +203,14 @@ def build_binary(args):
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'
old_plat = True build = True
if 'magiskboot' in args.target: if 'magiskboot' in args.target:
flags += ' B_BOOT=1' flags += ' B_BOOT=1'
old_plat = True build = True
if old_plat: if build:
proc = system('{} -C native {} -j{}'.format(ndk_build, flags, cpu_count)) proc = system(f'{ndk_build} -C native {flags} -j{cpu_count}')
if proc.returncode != 0:
error('Build binaries failed!')
collect_binary()
if 'busybox' in args.target:
proc = system('{} -C native {} B_BB=1 -j{}'.format(ndk_build, base_flags, cpu_count))
if proc.returncode != 0: if proc.returncode != 0:
error('Build binaries failed!') error('Build binaries failed!')
collect_binary() collect_binary()
@ -226,13 +220,13 @@ def build_apk(args, flavor):
buildType = 'Release' if args.release else 'Debug' buildType = 'Release' if args.release else 'Debug'
proc = execv([gradlew, 'app:assemble' + flavor + buildType, '-PconfigPath=' + os.path.abspath(args.config)]) proc = execv([gradlew, f'app:assemble{flavor}{buildType}', '-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() buildType = buildType.lower()
apk = 'app-{}-{}.apk'.format(flavor, buildType) apk = f'app-{flavor}-{buildType}.apk'
source = os.path.join('app', 'build', 'outputs', 'apk', flavor, buildType, apk) source = os.path.join('app', 'build', 'outputs', 'apk', flavor, buildType, apk)
target = os.path.join(config['outdir'], apk) target = os.path.join(config['outdir'], apk)
@ -306,9 +300,9 @@ def zip_main(args):
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',
'MAGISK_VER="{}"\nMAGISK_VER_CODE={}'.format(config['version'], 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('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')
@ -322,7 +316,7 @@ def zip_main(args):
# End of zipping # End of zipping
output = os.path.join(config['outdir'], 'Magisk-v{}.zip'.format(config['version']) if config['prettyName'] else 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') 'magisk-release.zip' if args.release else 'magisk-debug.zip')
sign_zip(unsigned, output, args.release) sign_zip(unsigned, output, args.release)
header('Output: ' + output) header('Output: ' + output)
@ -354,9 +348,8 @@ def zip_uninstaller(args):
# 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:
# Remove the stub
target = os.path.join('util_functions.sh') target = os.path.join('util_functions.sh')
vprint('zip: ' + source + ' -> ' + target) vprint(f'zip: {source} -> {target}')
zipf.writestr(target, script.read()) zipf.writestr(target, script.read())
# Prebuilts # Prebuilts
@ -366,7 +359,7 @@ def zip_uninstaller(args):
# End of zipping # End of zipping
output = os.path.join(config['outdir'], 'Magisk-uninstaller-{}.zip'.format(datetime.datetime.now().strftime('%Y%m%d')) output = os.path.join(config['outdir'], f'Magisk-uninstaller-{datetime.datetime.now().strftime("%Y%m%d")}.zip'
if config['prettyName'] else 'magisk-uninstaller.zip') if config['prettyName'] else 'magisk-uninstaller.zip')
sign_zip(unsigned, output, args.release) sign_zip(unsigned, output, args.release)
header('Output: ' + output) header('Output: ' + output)
@ -407,7 +400,7 @@ all_parser = subparsers.add_parser('all', help='build everything (binaries/apks/
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, b64xz. 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')
@ -459,6 +452,6 @@ 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('Please generate a java keystore and place it in \'{}\''.format(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)

View File

@ -115,19 +115,6 @@ include $(BUILD_EXECUTABLE)
endif endif
ifdef B_BXZ
# b64xz
include $(CLEAR_VARS)
LOCAL_MODULE := b64xz
LOCAL_STATIC_LIBRARIES := libxz
LOCAL_C_INCLUDES := $(EXT_PATH)/include
LOCAL_SRC_FILES := misc/b64xz.c
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
endif
ifdef B_BB ifdef B_BB
# Busybox # Busybox

View File

@ -1,78 +0,0 @@
/* b64xz.c - Base64-XZ Extractor
*
* This program converts data from stdin to stdout.
* The input should be compressed with xz (integrity check only support CRC32 or none) and then
* encoded into base64 format. What b64xz does is basically the reverse of the
* mentioned process: decode base64 to bytes, decompress xz, then dump to stdout
*
* The compiled binary will be hex-dumped into update-binary
* Busybox will be xz-compressed, base64 encoded and dumped into update-binary
* This program is to recover busybox for Magisk installation environment
*/
#include <unistd.h>
#include <xz.h>
static const char trans_tbl[] =
"|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
static void decodeblock(uint8_t* in, uint8_t* out) {
out[0] = (uint8_t)(in[0] << 2 | in[1] >> 4);
out[1] = (uint8_t)(in[1] << 4 | in[2] >> 2);
out[2] = (uint8_t)(((in[2] << 6) & 0xc0) | in[3]);
}
static int unxz(struct xz_dec *dec, const void *buf, unsigned size) {
uint8_t out[8192];
struct xz_buf b = {
.in = buf,
.in_pos = 0,
.in_size = size,
.out = out,
.out_pos = 0,
.out_size = sizeof(out)
};
enum xz_ret ret;
do {
ret = xz_dec_run(dec, &b);
if (ret != XZ_OK && ret != XZ_STREAM_END)
return 1;
write(STDOUT_FILENO, out, b.out_pos);
b.out_pos = 0;
} while (b.in_pos != size);
return 0;
}
int main(int argc, char const* argv[]) {
if (argc > 1)
return 0;
uint8_t in[4], buf[6144];
unsigned len = 0, pos = 0;
int8_t c;
xz_crc32_init();
struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);
if (dec == NULL)
return 1;
while (read(STDIN_FILENO, &c, sizeof(c)) == 1) {
c = ((c < 43 || c > 122) ? -1 : (trans_tbl[c - 43] == '$' ? -1 : trans_tbl[c - 43] - 62));
if (c >= 0)
in[len++] = c;
if (len < 4)
continue;
len = 0;
decodeblock(in, buf + pos);
pos += 3;
if (pos == sizeof(buf)) {
if (unxz(dec, buf, pos))
return 1;
pos = 0;
}
}
if (pos && unxz(dec, buf, pos))
return 1;
xz_dec_end(dec);
return 0;
}

View File

@ -1,34 +1,37 @@
# EX_ARM, EX_X86, BB_ARM, and BB_X86 should be generated in build.py #!/sbin/sh
X86_CNT=__X86_CNT__
extract_bb() { extract_bb() {
EXBIN=$BBDIR/b64xz; BBBIN=$BBDIR/busybox case `uname -m` in
touch $EXBIN; touch $BBBIN; chmod 755 $EXBIN $BBBIN x*) dd if="$0" of="$BBBIN" bs=1024 skip=1 count=$X86_CNT ;;
echo -ne $EX_ARM > $EXBIN *) dd if="$0" of="$BBBIN" bs=1024 skip=$(($X86_CNT + 1));;
if $EXBIN --test 2>/dev/null; then esac
echo $BB_ARM | $EXBIN > $BBBIN chmod 755 "$BBBIN"
else
echo -ne $EX_X86 > $EXBIN
echo $BB_X86 | $EXBIN > $BBBIN
fi
rm $EXBIN
} }
setup_bb() { setup_bb() {
BBDIR=$TMPDIR/bin; mkdir -p $BBDIR 2>/dev/null BBDIR=$TMPDIR/bin
BBBIN=$BBDIR/busybox
mkdir -p $BBDIR 2>/dev/null
extract_bb extract_bb
$BBBIN --install -s $BBDIR $BBBIN --install -s $BBDIR
export PATH=$BBDIR:$PATH export PATH=$BBDIR:$PATH
} }
case "$1" in case "$1" in
"extract") "extract"|"-x")
[ -z "$2" ] && BBDIR=. || BBDIR="$2" [ -z "$2" ] && BBBIN=./busybox || BBBIN="$2"
extract_bb extract_bb
;; ;;
"indep") "indep"|"sh")
TMPDIR=.; setup_bb; shift TMPDIR=.;setup_bb;shift
exec /system/bin/sh "$@" exec /system/bin/sh "$@"
;; ;;
*) *)
export TMPDIR=/dev/tmp; rm -rf $TMPDIR 2>/dev/null; setup_bb export TMPDIR=/dev/tmp
export INSTALLER=$TMPDIR/install; mkdir -p $INSTALLER; unzip -o "$3" -d $INSTALLER >&2 rm -rf $TMPDIR 2>/dev/null
setup_bb
export INSTALLER=$TMPDIR/install
mkdir -p $INSTALLER
unzip -o "$3" -d $INSTALLER >&2
exec sh $INSTALLER/META-INF/com/google/android/updater-script "$@" exec sh $INSTALLER/META-INF/com/google/android/updater-script "$@"
;; ;;
esac esac
exit