Magisk/build.py

705 lines
20 KiB
Python
Raw Normal View History

2017-06-03 17:39:52 +00:00
#!/usr/bin/env python3
2020-12-27 00:04:41 +00:00
import argparse
import errno
import lzma
2021-05-12 05:40:53 +00:00
import multiprocessing
import os
2020-12-27 00:04:41 +00:00
import os.path as op
2021-05-12 05:40:53 +00:00
import platform
import shutil
import stat
2021-05-12 05:40:53 +00:00
import subprocess
import sys
import textwrap
2021-05-12 05:40:53 +00:00
import urllib.request
2022-04-15 17:04:04 +00:00
import tarfile
2023-05-02 23:28:02 +00:00
from zipfile import ZipFile
2017-11-11 20:17:56 +00:00
2023-05-12 04:01:04 +00:00
def color_print(code, str):
2022-08-17 08:59:23 +00:00
if no_color:
2023-05-12 04:01:04 +00:00
print(f"\n{str}\n")
2020-12-25 23:54:47 +00:00
else:
2023-05-12 04:01:04 +00:00
str.replace("\n", "\033[K\n")
print(f"{code}{str}\033[0m")
def error(str):
color_print("\033[41;39m", f"\n! {str}\n")
sys.exit(1)
2017-06-03 12:19:01 +00:00
def header(str):
2023-05-12 04:01:04 +00:00
color_print("\033[44;39m", f"\n{str}\n")
2017-06-03 12:19:01 +00:00
2018-08-05 18:01:04 +00:00
def vprint(str):
if args.verbose:
print(str)
2018-08-05 18:01:04 +00:00
2023-05-11 09:39:57 +00:00
is_windows = os.name == "nt"
EXE_EXT = ".exe" if is_windows else ""
2020-12-27 00:04:41 +00:00
2023-04-24 20:46:25 +00:00
no_color = False
if is_windows:
2022-08-17 08:59:23 +00:00
try:
import colorama
2023-05-11 09:39:57 +00:00
2022-08-17 08:59:23 +00:00
colorama.init()
except ImportError:
# We can't do ANSI color codes in terminal on Windows without colorama
no_color = True
2020-12-27 00:04:41 +00:00
2017-06-03 12:19:01 +00:00
# Environment checks
2022-12-26 08:09:27 +00:00
if not sys.version_info >= (3, 8):
2023-05-11 09:39:57 +00:00
error("Requires Python 3.8+")
2017-06-03 12:19:01 +00:00
2023-05-11 09:39:57 +00:00
if "ANDROID_SDK_ROOT" not in os.environ:
2023-05-12 04:01:04 +00:00
error("Please set Android SDK path to environment variable ANDROID_SDK_ROOT!")
2017-06-03 12:19:01 +00:00
2018-05-12 19:04:40 +00:00
cpu_count = multiprocessing.cpu_count()
2023-05-11 09:39:57 +00:00
archs = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"]
triples = [
"armv7a-linux-androideabi",
"i686-linux-android",
"aarch64-linux-android",
"x86_64-linux-android",
]
default_targets = ["magisk", "magiskinit", "magiskboot", "magiskpolicy", "busybox"]
support_targets = default_targets + ["resetprop"]
rust_targets = ["magisk", "magiskinit", "magiskboot", "magiskpolicy"]
sdk_path = os.environ["ANDROID_SDK_ROOT"]
ndk_root = op.join(sdk_path, "ndk")
ndk_path = op.join(ndk_root, "magisk")
ndk_build = op.join(ndk_path, "ndk-build")
rust_bin = op.join(ndk_path, "toolchains", "rust", "bin")
cargo = op.join(rust_bin, "cargo" + EXE_EXT)
gradlew = op.join(".", "gradlew" + (".bat" if is_windows else ""))
adb_path = op.join(sdk_path, "platform-tools", "adb" + EXE_EXT)
native_gen_path = op.realpath(op.join("native", "out", "generated"))
2018-05-12 19:04:40 +00:00
2019-10-17 22:02:31 +00:00
# Global vars
config = {}
STDOUT = None
2020-12-27 00:04:41 +00:00
build_tools = None
def mv(source, target):
try:
shutil.move(source, target)
2023-05-11 09:39:57 +00:00
vprint(f"mv {source} -> {target}")
2019-02-11 22:14:29 +00:00
except:
pass
def cp(source, target):
try:
shutil.copyfile(source, target)
2023-05-11 09:39:57 +00:00
vprint(f"cp {source} -> {target}")
2019-02-11 22:14:29 +00:00
except:
pass
def rm(file):
try:
os.remove(file)
2023-05-11 09:39:57 +00:00
vprint(f"rm {file}")
except OSError as e:
if e.errno != errno.ENOENT:
raise
2017-06-03 12:19:01 +00:00
def rm_on_error(func, path, _):
# Remove a read-only file on Windows will get "WindowsError: [Error 5] Access is denied"
# Clear the "read-only" and retry
os.chmod(path, stat.S_IWRITE)
os.unlink(path)
2020-04-03 10:33:39 +00:00
def rm_rf(path):
2023-05-11 09:39:57 +00:00
vprint(f"rm -rf {path}")
shutil.rmtree(path, ignore_errors=True, onerror=rm_on_error)
2020-04-03 10:33:39 +00:00
def mkdir(path, mode=0o755):
try:
os.mkdir(path, mode)
2019-02-11 22:14:29 +00:00
except:
pass
2020-04-03 10:33:39 +00:00
def mkdir_p(path, mode=0o755):
os.makedirs(path, mode, exist_ok=True)
2022-06-30 21:50:21 +00:00
def execv(cmd, env=None):
return subprocess.run(cmd, stdout=STDOUT, env=env)
def system(cmd):
return subprocess.run(cmd, shell=True, stdout=STDOUT)
2022-06-30 21:50:21 +00:00
def cmd_out(cmd, env=None):
2023-05-11 09:39:57 +00:00
return (
subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, env=env)
.stdout.strip()
.decode("utf-8")
)
2020-12-24 12:46:31 +00:00
def xz(data):
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
2020-04-03 10:33:39 +00:00
def parse_props(file):
props = {}
2023-05-11 09:39:57 +00:00
with open(file, "r") as f:
for line in [l.strip(" \t\r\n") for l in f]:
if line.startswith("#") or len(line) == 0:
2020-04-03 10:33:39 +00:00
continue
2023-05-11 09:39:57 +00:00
prop = line.split("=")
2020-04-03 10:33:39 +00:00
if len(prop) != 2:
continue
2023-05-11 09:39:57 +00:00
value = prop[1].strip(" \t\r\n")
2020-12-24 12:46:31 +00:00
if len(value) == 0:
continue
2023-05-11 09:39:57 +00:00
props[prop[0].strip(" \t\r\n")] = value
2020-04-03 10:33:39 +00:00
return props
def load_config(args):
2023-05-11 09:39:57 +00:00
commit_hash = cmd_out(["git", "rev-parse", "--short=8", "HEAD"])
2020-04-03 10:33:39 +00:00
2020-12-25 13:34:15 +00:00
# Default values
2023-05-11 09:39:57 +00:00
config["version"] = commit_hash
config["outdir"] = "out"
2020-12-25 13:34:15 +00:00
# Load prop files
2020-12-24 12:46:31 +00:00
if op.exists(args.config):
config.update(parse_props(args.config))
2019-10-23 10:57:47 +00:00
2023-05-11 09:39:57 +00:00
for key, value in parse_props("gradle.properties").items():
if key.startswith("magisk."):
2020-12-25 13:34:15 +00:00
config[key[7:]] = value
try:
2023-05-11 09:39:57 +00:00
config["versionCode"] = int(config["versionCode"])
except ValueError:
error('Config error: "versionCode" is required to be an integer')
2023-05-11 09:39:57 +00:00
mkdir_p(config["outdir"])
global STDOUT
STDOUT = None if args.verbose else subprocess.DEVNULL
def clean_elf():
2019-10-17 22:02:31 +00:00
if is_windows:
2023-05-11 09:39:57 +00:00
elf_cleaner = op.join("tools", "elf-cleaner.exe")
else:
2023-05-11 09:39:57 +00:00
elf_cleaner = op.join("native", "out", "elf-cleaner")
2020-04-03 10:33:39 +00:00
if not op.exists(elf_cleaner):
2023-05-11 09:39:57 +00:00
execv(
[
"gcc",
'-DPACKAGE_NAME="termux-elf-cleaner"',
'-DPACKAGE_VERSION="2.1.1"',
'-DCOPYRIGHT="Copyright (C) 2022 Termux."',
"tools/termux-elf-cleaner/elf-cleaner.cpp",
"tools/termux-elf-cleaner/arghandling.c",
"-o",
elf_cleaner,
]
)
args = [elf_cleaner, "--api-level", "23"]
2023-05-11 09:39:57 +00:00
args.extend(
op.join("native", "out", arch, bin)
for arch in archs
for bin in ["magisk", "magiskpolicy"]
)
execv(args)
def run_ndk_build(flags):
2023-05-11 09:39:57 +00:00
os.chdir("native")
flags = "NDK_PROJECT_PATH=. NDK_APPLICATION_MK=src/Application.mk " + flags
proc = system(f"{ndk_build} {flags} -j{cpu_count}")
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("Build binary failed!")
os.chdir("..")
2022-06-30 21:50:21 +00:00
for arch in archs:
2023-05-11 09:39:57 +00:00
for tgt in support_targets + ["libinit-ld.so", "libzygisk-ld.so"]:
source = op.join("native", "libs", arch, tgt)
target = op.join("native", "out", arch, tgt)
2022-06-30 21:50:21 +00:00
mv(source, target)
def run_cargo_build(args):
2023-05-11 09:39:57 +00:00
os.chdir(op.join("native", "src"))
2022-06-30 21:50:21 +00:00
targets = set(args.target) & set(rust_targets)
2023-05-11 09:39:57 +00:00
if "resetprop" in args.target:
targets.add("magisk")
2022-06-30 21:50:21 +00:00
env = os.environ.copy()
2023-05-11 09:39:57 +00:00
env["CARGO_BUILD_RUSTC"] = op.join(rust_bin, "rustc" + EXE_EXT)
2022-06-30 21:50:21 +00:00
# Install cxxbridge and generate C++ bindings
2023-05-11 09:39:57 +00:00
native_out = op.join("..", "out")
local_cargo_root = op.join(native_out, ".cargo")
cfg = op.join(".cargo", "config.toml")
cfg_bak = op.join(".cargo", "config.toml.bak")
2022-08-17 08:59:23 +00:00
try:
# Hide the config file for cargo install
mv(cfg, cfg_bak)
2023-05-11 09:39:57 +00:00
cxx_src = op.join("external", "cxx-rs", "gen", "cmd")
2022-08-17 08:59:23 +00:00
mkdir_p(local_cargo_root)
2023-05-11 09:39:57 +00:00
cmds = [cargo, "install", "--root", local_cargo_root, "--path", cxx_src]
2022-08-17 08:59:23 +00:00
if not args.verbose:
2023-05-11 09:39:57 +00:00
cmds.append("-q")
2022-08-17 08:59:23 +00:00
proc = execv(cmds, env)
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("cxxbridge-cmd installation failed!")
2022-08-17 08:59:23 +00:00
finally:
# Make sure the config file rename is always reverted
mv(cfg_bak, cfg)
2023-05-11 09:39:57 +00:00
cxxbridge = op.join(local_cargo_root, "bin", "cxxbridge" + EXE_EXT)
2022-06-30 21:50:21 +00:00
mkdir(native_gen_path)
2023-05-11 09:39:57 +00:00
for p in ["base", "boot", "core", "init", "sepolicy"]:
text = cmd_out([cxxbridge, op.join(p, "lib.rs")])
write_if_diff(op.join(native_gen_path, f"{p}-rs.cpp"), text)
text = cmd_out([cxxbridge, "--header", op.join(p, "lib.rs")])
write_if_diff(op.join(native_gen_path, f"{p}-rs.hpp"), text)
2022-06-30 21:50:21 +00:00
# Start building the actual build commands
2023-05-11 09:39:57 +00:00
cmds = [cargo, "build"]
2022-06-30 21:50:21 +00:00
for target in targets:
2023-05-11 09:39:57 +00:00
cmds.append("-p")
2022-06-30 21:50:21 +00:00
cmds.append(target)
2023-05-11 09:39:57 +00:00
rust_out = "debug"
2022-06-30 21:50:21 +00:00
if args.release:
2023-05-11 09:39:57 +00:00
cmds.append("-r")
rust_out = "release"
2022-06-30 21:50:21 +00:00
if not args.verbose:
2023-05-11 09:39:57 +00:00
cmds.append("-q")
2022-06-30 21:50:21 +00:00
os_name = platform.system().lower()
2023-05-11 09:39:57 +00:00
llvm_bin = op.join(
ndk_path, "toolchains", "llvm", "prebuilt", f"{os_name}-x86_64", "bin"
)
env["TARGET_CC"] = op.join(llvm_bin, "clang" + EXE_EXT)
env["RUSTFLAGS"] = "-Clinker-plugin-lto"
for arch, triple in zip(archs, triples):
env["TARGET_CFLAGS"] = f"--target={triple}23"
rust_triple = (
"thumbv7neon-linux-androideabi" if triple.startswith("armv7") else triple
)
proc = execv([*cmds, "--target", rust_triple], env)
2022-06-30 21:50:21 +00:00
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("Build binary failed!")
2022-06-30 21:50:21 +00:00
arch_out = op.join(native_out, arch)
mkdir(arch_out)
for tgt in targets:
2023-05-11 09:39:57 +00:00
source = op.join("target", rust_triple, rust_out, f"lib{tgt}.a")
target = op.join(arch_out, f"lib{tgt}-rs.a")
2022-06-30 21:50:21 +00:00
mv(source, target)
2023-05-11 09:39:57 +00:00
os.chdir(op.join("..", ".."))
def write_if_diff(file_name, text):
do_write = True
if op.exists(file_name):
2023-05-11 09:39:57 +00:00
with open(file_name, "r") as f:
orig = f.read()
do_write = orig != text
if do_write:
2023-05-11 09:39:57 +00:00
with open(file_name, "w") as f:
f.write(text)
def binary_dump(src, var_name, compressor=xz):
2023-05-11 09:39:57 +00:00
out_str = f"constexpr unsigned char {var_name}[] = {{"
for i, c in enumerate(compressor(src.read())):
if i % 16 == 0:
2023-05-11 09:39:57 +00:00
out_str += "\n"
out_str += f"0x{c:02X},"
out_str += "\n};\n"
return out_str
2022-05-15 08:01:54 +00:00
def dump_bin_header(args):
mkdir_p(native_gen_path)
for arch in archs:
2023-05-11 09:39:57 +00:00
preload = op.join("native", "out", arch, "libinit-ld.so")
with open(preload, "rb") as src:
text = binary_dump(src, "init_ld_xz")
preload = op.join("native", "out", arch, "libzygisk-ld.so")
with open(preload, "rb") as src:
text += binary_dump(src, "zygisk_ld", compressor=lambda x: x)
write_if_diff(op.join(native_gen_path, f"{arch}_binaries.h"), text)
def dump_flag_header():
2023-05-11 09:39:57 +00:00
flag_txt = textwrap.dedent(
"""\
#pragma once
#define quote(s) #s
#define str(s) quote(s)
#define MAGISK_FULL_VER MAGISK_VERSION "(" str(MAGISK_VER_CODE) ")"
#define NAME_WITH_VER(name) str(name) " " MAGISK_FULL_VER
2023-05-11 09:39:57 +00:00
"""
)
flag_txt += f'#define MAGISK_VERSION "{config["version"]}"\n'
flag_txt += f'#define MAGISK_VER_CODE {config["versionCode"]}\n'
2023-05-11 09:39:57 +00:00
flag_txt += f"#define MAGISK_DEBUG {0 if args.release else 1}\n"
mkdir_p(native_gen_path)
2023-05-11 09:39:57 +00:00
write_if_diff(op.join(native_gen_path, "flags.h"), flag_txt)
2020-03-28 06:23:26 +00:00
2018-05-12 19:04:40 +00:00
def build_binary(args):
2020-04-03 10:33:39 +00:00
# Verify NDK install
2022-04-15 17:04:04 +00:00
try:
2023-05-11 09:39:57 +00:00
with open(op.join(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
assert ondk_ver.read().strip(" \t\r\n") == config["ondkVersion"]
2022-04-15 17:04:04 +00:00
except:
error('Unmatched NDK. Please install/upgrade NDK with "build.py ndk"')
2020-04-03 10:33:39 +00:00
2023-05-11 09:39:57 +00:00
if "target" not in vars(args):
vars(args)["target"] = []
if args.target:
2019-05-31 04:17:58 +00:00
args.target = set(args.target) & set(support_targets)
if not args.target:
return
else:
2019-05-31 04:17:58 +00:00
args.target = default_targets
2023-05-11 09:39:57 +00:00
header("* Building binaries: " + " ".join(args.target))
2022-06-30 21:50:21 +00:00
run_cargo_build(args)
dump_flag_header()
2023-05-11 09:39:57 +00:00
flag = ""
2021-05-12 05:40:53 +00:00
2023-05-11 09:39:57 +00:00
if "magisk" in args.target or "magiskinit" in args.target:
flag += " B_PRELOAD=1"
2023-05-11 09:39:57 +00:00
if "magiskpolicy" in args.target:
flag += " B_POLICY=1"
2022-03-17 10:15:39 +00:00
2023-05-11 09:39:57 +00:00
if "test" in args.target:
flag += " B_TEST=1"
2021-01-18 12:25:26 +00:00
2023-05-11 09:39:57 +00:00
if "magiskinit" in args.target:
flag += " B_PRELOAD=1"
2023-05-11 09:39:57 +00:00
if "resetprop" in args.target:
flag += " B_PROP=1"
2020-01-20 16:48:52 +00:00
2023-05-11 09:39:57 +00:00
if "magiskboot" in args.target:
flag += " B_BOOT=1"
2021-01-18 12:25:26 +00:00
if flag:
run_ndk_build(flag)
2022-05-30 10:47:31 +00:00
# magiskinit and magisk embeds preload.so
2023-05-11 09:39:57 +00:00
flag = ""
2023-05-11 09:39:57 +00:00
if "magisk" in args.target:
flag += " B_MAGISK=1"
2022-05-30 10:47:31 +00:00
2023-05-11 09:39:57 +00:00
if "magiskinit" in args.target:
flag += " B_INIT=1"
if flag:
2022-05-30 10:47:31 +00:00
dump_bin_header(args)
run_ndk_build(flag)
clean_elf()
# BusyBox is built with different libc
2023-05-11 09:39:57 +00:00
if "busybox" in args.target:
run_ndk_build("B_BB=1")
2021-05-12 05:40:53 +00:00
2023-05-12 04:01:04 +00:00
def find_jdk():
env = os.environ.copy()
if "ANDROID_STUDIO" in env:
studio = env["ANDROID_STUDIO"]
jbr = op.join(studio, "jbr", "bin")
if not op.exists(jbr):
jbr = op.join(studio, "Contents", "jbr", "Contents", "Home", "bin")
if op.exists(jbr):
env["PATH"] = f'{jbr}:{env["PATH"]}'
no_jdk = False
try:
proc = subprocess.run(
["javac", "-version"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
env=env,
)
no_jdk = proc.returncode != 0
except FileNotFoundError:
no_jdk = True
if no_jdk:
error(
"Please set Android Studio's path to environment variable ANDROID_STUDIO,\n"
+ "or install JDK 17 and make sure 'javac' is available in PATH"
)
return env
def build_apk(args, module):
2023-05-12 04:01:04 +00:00
env = find_jdk()
2023-05-11 09:39:57 +00:00
2023-05-12 04:01:04 +00:00
build_type = "Release" if args.release else "Debug"
2023-05-11 09:39:57 +00:00
proc = execv(
[
gradlew,
f"{module}:assemble{build_type}",
"-PconfigPath=" + op.abspath(args.config),
2023-05-12 04:01:04 +00:00
],
env=env,
2023-05-11 09:39:57 +00:00
)
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error(f"Build {module} failed!")
2017-06-03 14:04:22 +00:00
build_type = build_type.lower()
2023-05-11 09:39:57 +00:00
apk = f"{module}-{build_type}.apk"
source = op.join(module, "build", "outputs", "apk", build_type, apk)
target = op.join(config["outdir"], apk)
2021-05-13 07:21:04 +00:00
mv(source, target)
2023-05-11 09:39:57 +00:00
header("Output: " + target)
2018-08-09 21:54:26 +00:00
2018-08-20 04:02:38 +00:00
def build_app(args):
2023-05-11 09:39:57 +00:00
header("* Building the Magisk app")
build_apk(args, "app")
2022-12-26 23:23:06 +00:00
# Stub building is directly integrated into the main app
# build process. Copy the stub APK into output directory.
2023-05-11 09:39:57 +00:00
build_type = "release" if args.release else "debug"
apk = f"stub-{build_type}.apk"
source = op.join("app", "src", build_type, "assets", "stub.apk")
target = op.join(config["outdir"], apk)
2022-12-26 23:23:06 +00:00
cp(source, target)
def build_stub(args):
2023-05-11 09:39:57 +00:00
header("* Building the stub app")
build_apk(args, "stub")
2022-12-26 23:23:06 +00:00
2018-05-27 06:55:24 +00:00
2017-06-03 12:19:01 +00:00
def cleanup(args):
2023-05-11 09:39:57 +00:00
support_targets = {"native", "java"}
if args.target:
args.target = set(args.target) & support_targets
else:
args.target = support_targets
2023-05-11 09:39:57 +00:00
if "native" in args.target:
header("* Cleaning native")
rm_rf(op.join("native", "libs"))
rm_rf(op.join("native", "obj"))
rm_rf(op.join("native", "out"))
rm_rf(op.join("native", "src", "target"))
rm_rf(op.join("native", "src", "external", "cxx-rs", "target"))
2023-05-11 09:39:57 +00:00
if "java" in args.target:
header("* Cleaning java")
execv([gradlew, "app:clean", "app:shared:clean", "stub:clean"])
rm_rf(op.join("app", "src", "debug"))
rm_rf(op.join("app", "src", "release"))
2018-05-12 19:04:40 +00:00
2020-04-03 10:33:39 +00:00
def setup_ndk(args):
os_name = platform.system().lower()
2023-05-11 09:39:57 +00:00
ndk_ver = config["ondkVersion"]
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.gz"
ndk_archive = url.split("/")[-1]
2020-04-03 10:33:39 +00:00
2023-05-11 09:39:57 +00:00
header(f"* Downloading and extracting {ndk_archive}")
2022-04-15 17:04:04 +00:00
with urllib.request.urlopen(url) as response:
2023-05-11 09:39:57 +00:00
with tarfile.open(mode="r|gz", fileobj=response) as tar:
2022-04-15 17:04:04 +00:00
tar.extractall(ndk_root)
2020-04-03 10:33:39 +00:00
rm_rf(ndk_path)
2023-05-11 09:39:57 +00:00
mv(op.join(ndk_root, f"ondk-{ndk_ver}"), ndk_path)
2020-04-03 10:33:39 +00:00
2023-05-11 09:39:57 +00:00
header("* Patching static libs")
for target in ["arm-linux-androideabi", "i686-linux-android"]:
arch = target.split("-")[0]
2021-10-20 10:17:42 +00:00
lib_dir = op.join(
2023-05-11 09:39:57 +00:00
ndk_path,
"toolchains",
"llvm",
"prebuilt",
f"{os_name}-x86_64",
"sysroot",
"usr",
"lib",
f"{target}",
"23",
)
2021-10-20 10:17:42 +00:00
if not op.exists(lib_dir):
continue
2023-05-11 09:39:57 +00:00
src_dir = op.join("tools", "ndk-bins", "21", arch)
rm(op.join(src_dir, ".DS_Store"))
2022-04-15 17:04:04 +00:00
shutil.copytree(src_dir, lib_dir, copy_function=cp, dirs_exist_ok=True)
2020-04-03 10:33:39 +00:00
2023-05-02 23:28:02 +00:00
def push_files(args, script):
2023-05-11 09:39:57 +00:00
abi = cmd_out([adb_path, "shell", "getprop", "ro.product.cpu.abi"])
apk = config["outdir"] + ("/app-release.apk" if args.release else "/app-debug.apk")
2023-05-02 23:28:02 +00:00
# Extract busybox from APK
busybox = f'{config["outdir"]}/busybox'
with ZipFile(apk) as zf:
2023-05-11 09:39:57 +00:00
with zf.open(f"lib/{abi}/libbusybox.so") as libbb:
with open(busybox, "wb") as bb:
2023-05-02 23:28:02 +00:00
bb.write(libbb.read())
2023-05-02 23:28:02 +00:00
try:
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "push", busybox, script, "/data/local/tmp"])
2023-05-02 23:28:02 +00:00
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("adb push failed!")
2023-05-02 23:28:02 +00:00
finally:
rm_rf(busybox)
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "push", apk, "/data/local/tmp/magisk.apk"])
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("adb push failed!")
2023-05-02 23:28:02 +00:00
def setup_avd(args):
if not args.skip:
build_all(args)
2023-05-11 09:39:57 +00:00
header("* Setting up emulator")
2023-05-02 23:28:02 +00:00
2023-05-11 09:39:57 +00:00
push_files(args, "scripts/avd_magisk.sh")
2023-05-02 23:28:02 +00:00
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "shell", "sh", "/data/local/tmp/avd_magisk.sh"])
2022-01-19 13:12:11 +00:00
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("avd_magisk.sh failed!")
2022-01-19 13:12:11 +00:00
def patch_avd_ramdisk(args):
if not args.skip:
2022-05-20 05:54:49 +00:00
args.release = False
build_all(args)
2022-01-19 13:12:11 +00:00
2023-05-11 09:39:57 +00:00
header("* Patching emulator ramdisk.img")
2022-01-19 13:12:11 +00:00
# Create a backup to prevent accidental overwrites
2023-05-11 09:39:57 +00:00
backup = args.ramdisk + ".bak"
2022-01-19 13:12:11 +00:00
if not op.exists(backup):
cp(args.ramdisk, backup)
2023-05-11 09:39:57 +00:00
ini = op.join(op.dirname(args.ramdisk), "advancedFeatures.ini")
with open(ini, "r") as f:
2022-01-19 13:12:11 +00:00
adv_ft = f.read()
# Need to turn off system as root
2023-05-11 09:39:57 +00:00
if "SystemAsRoot = on" in adv_ft:
2022-01-19 13:12:11 +00:00
# Create a backup
2023-05-11 09:39:57 +00:00
cp(ini, ini + ".bak")
adv_ft = adv_ft.replace("SystemAsRoot = on", "SystemAsRoot = off")
with open(ini, "w") as f:
2022-01-19 13:12:11 +00:00
f.write(adv_ft)
2023-05-11 09:39:57 +00:00
push_files(args, "scripts/avd_patch.sh")
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "push", backup, "/data/local/tmp/ramdisk.cpio.tmp"])
2022-01-19 13:12:11 +00:00
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("adb push failed!")
2022-01-19 13:12:11 +00:00
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "shell", "sh", "/data/local/tmp/avd_patch.sh"])
2022-01-19 13:12:11 +00:00
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("avd_patch.sh failed!")
2022-01-19 13:12:11 +00:00
2023-05-11 09:39:57 +00:00
proc = execv([adb_path, "pull", "/data/local/tmp/ramdisk.cpio.gz", args.ramdisk])
if proc.returncode != 0:
2023-05-11 09:39:57 +00:00
error("adb pull failed!")
2018-08-09 21:54:26 +00:00
def build_all(args):
build_binary(args)
build_app(args)
2018-08-09 21:54:26 +00:00
2023-05-11 09:39:57 +00:00
parser = argparse.ArgumentParser(description="Magisk build script")
2020-12-27 00:04:41 +00:00
parser.set_defaults(func=lambda x: None)
2023-05-11 09:39:57 +00:00
parser.add_argument(
"-r", "--release", action="store_true", help="compile in release mode"
)
parser.add_argument("-v", "--verbose", action="store_true", help="verbose output")
parser.add_argument(
"-c",
"--config",
default="config.prop",
help="custom config file (default: config.prop)",
)
subparsers = parser.add_subparsers(title="actions")
all_parser = subparsers.add_parser("all", help="build everything")
2017-06-03 12:19:01 +00:00
all_parser.set_defaults(func=build_all)
2023-05-11 09:39:57 +00:00
binary_parser = subparsers.add_parser("binary", help="build binaries")
binary_parser.add_argument(
2023-05-11 09:39:57 +00:00
"target",
nargs="*",
help=f"{', '.join(support_targets)}, \
or empty for defaults ({', '.join(default_targets)})",
)
2017-06-03 12:19:01 +00:00
binary_parser.set_defaults(func=build_binary)
2023-05-11 09:39:57 +00:00
app_parser = subparsers.add_parser("app", help="build the Magisk app")
app_parser.set_defaults(func=build_app)
2017-06-03 12:19:01 +00:00
2023-05-11 09:39:57 +00:00
stub_parser = subparsers.add_parser("stub", help="build the stub app")
2022-12-26 23:23:06 +00:00
stub_parser.set_defaults(func=build_stub)
2023-05-11 09:39:57 +00:00
avd_parser = subparsers.add_parser("emulator", help="setup AVD for development")
avd_parser.add_argument(
"-s", "--skip", action="store_true", help="skip building binaries and the app"
)
avd_parser.set_defaults(func=setup_avd)
2023-05-11 09:39:57 +00:00
avd_patch_parser = subparsers.add_parser("avd_patch", help="patch AVD ramdisk.img")
avd_patch_parser.add_argument("ramdisk", help="path to ramdisk.img")
avd_patch_parser.add_argument(
"-s", "--skip", action="store_true", help="skip building binaries and the app"
)
2022-01-19 13:12:11 +00:00
avd_patch_parser.set_defaults(func=patch_avd_ramdisk)
2023-05-11 09:39:57 +00:00
clean_parser = subparsers.add_parser("clean", help="cleanup")
clean_parser.add_argument(
2023-05-11 09:39:57 +00:00
"target", nargs="*", help="native, java, or empty to clean both"
)
2017-06-03 12:19:01 +00:00
clean_parser.set_defaults(func=cleanup)
2023-05-11 09:39:57 +00:00
ndk_parser = subparsers.add_parser("ndk", help="setup Magisk NDK")
2020-04-03 10:33:39 +00:00
ndk_parser.set_defaults(func=setup_ndk)
2017-06-03 12:19:01 +00:00
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
2017-06-03 12:19:01 +00:00
args = parser.parse_args()
load_config(args)
# Call corresponding functions
2017-06-03 12:19:01 +00:00
args.func(args)