mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-04-17 16:36:58 -04:00
168 lines
4.7 KiB
Python
Executable File
168 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import functools
|
|
import multiprocessing
|
|
import os
|
|
import platform
|
|
import sys
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import NoReturn
|
|
|
|
|
|
ondk_version = "r29.5"
|
|
|
|
|
|
def color_print(code, str):
|
|
if no_color:
|
|
print(str)
|
|
else:
|
|
str = str.replace("\n", f"\033[0m\n{code}")
|
|
print(f"{code}{str}\033[0m")
|
|
|
|
|
|
def error(str) -> NoReturn:
|
|
color_print("\033[41;39m", f"\n! {str}\n")
|
|
sys.exit(1)
|
|
|
|
|
|
def header(str):
|
|
color_print("\033[44;39m", f"\n{str}\n")
|
|
|
|
|
|
class Paths:
|
|
def __init__(self):
|
|
try:
|
|
self.sdk = Path(os.environ["ANDROID_HOME"])
|
|
except KeyError:
|
|
try:
|
|
self.sdk = Path(os.environ["ANDROID_SDK_ROOT"])
|
|
except KeyError:
|
|
error(
|
|
"Please set Android SDK path to environment variable ANDROID_HOME"
|
|
)
|
|
|
|
self.ndk = self.sdk / "ndk" / "magisk"
|
|
self.ndk_build = self.ndk / "ndk-build"
|
|
self.rust_sysroot = self.ndk / "toolchains" / "rust"
|
|
self.adb = self.sdk / "platform-tools" / "adb"
|
|
project_root = Path(__file__).resolve().parent.parent
|
|
self.gradlew = project_root / "app" / "gradlew"
|
|
|
|
|
|
@functools.cache
|
|
def paths() -> Paths:
|
|
return Paths()
|
|
|
|
|
|
def run_once(func):
|
|
def wrapper(*args, **kwargs):
|
|
if not wrapper.has_run:
|
|
wrapper.has_run = True
|
|
return func(*args, **kwargs)
|
|
|
|
wrapper.has_run = False
|
|
return wrapper
|
|
|
|
|
|
@run_once
|
|
def ensure_toolchain():
|
|
# Verify NDK install
|
|
try:
|
|
with open(paths().ndk / "ONDK_VERSION", "r") as ondk_ver:
|
|
assert ondk_ver.read().strip(" \t\r\n") == ondk_version
|
|
except:
|
|
error('Unmatched NDK. Please install/upgrade NDK with "build.py ndk"')
|
|
|
|
if sccache := shutil.which("sccache"):
|
|
os.environ["RUSTC_WRAPPER"] = sccache
|
|
os.environ["NDK_CCACHE"] = sccache
|
|
os.environ["CARGO_INCREMENTAL"] = "0"
|
|
if ccache := shutil.which("ccache"):
|
|
os.environ["NDK_CCACHE"] = ccache
|
|
|
|
|
|
@run_once
|
|
def ensure_cargo():
|
|
ensure_toolchain()
|
|
|
|
os.environ["CARGO_BUILD_RUSTFLAGS"] = f"-Z threads={min(8, cpu_count)}"
|
|
if shutil.which("rustup"):
|
|
# Go through rustup proxies by default if available
|
|
os.environ["RUSTUP_TOOLCHAIN"] = str(paths().rust_sysroot)
|
|
else:
|
|
os.environ["PATH"] = (
|
|
f"{paths().rust_sysroot / "bin"}{os.pathsep}{os.environ["PATH"]}"
|
|
)
|
|
# Cargo calls executables in $RUSTROOT/lib/rustlib/$TRIPLE/bin, we need
|
|
# to make sure the runtime linker also search $RUSTROOT/lib for libraries.
|
|
# This is only required on Unix, as Windows search dlls from PATH.
|
|
if os_name == "darwin":
|
|
os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = str(paths().rust_sysroot / "lib")
|
|
elif os_name == "linux":
|
|
os.environ["LD_LIBRARY_PATH"] = str(paths().rust_sysroot / "lib")
|
|
|
|
|
|
@run_once
|
|
def ensure_jdk():
|
|
if "ANDROID_STUDIO" in os.environ:
|
|
studio = os.environ["ANDROID_STUDIO"]
|
|
jbr = Path(studio, "jbr")
|
|
if not jbr.exists():
|
|
jbr = Path(studio, "Contents", "jbr", "Contents", "Home")
|
|
if jbr.exists():
|
|
os.environ["PATH"] = f'{jbr / "bin"}{os.pathsep}{os.environ["PATH"]}'
|
|
os.environ["JAVA_HOME"] = str(jbr)
|
|
os.environ["JDK_HOME"] = str(jbr)
|
|
|
|
no_jdk = False
|
|
try:
|
|
proc = subprocess.run(
|
|
"javac -version",
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.DEVNULL,
|
|
shell=True,
|
|
)
|
|
output = proc.stdout.strip().decode("utf-8")
|
|
no_jdk = proc.returncode != 0 and output.startswith("javac 21")
|
|
except FileNotFoundError:
|
|
no_jdk = True
|
|
|
|
if no_jdk:
|
|
error(
|
|
"Please set Android Studio's path to environment variable ANDROID_STUDIO,\n"
|
|
+ "or install JDK 21 and make sure 'javac' is available in PATH"
|
|
)
|
|
|
|
|
|
# OS detection
|
|
os_name = platform.system().lower()
|
|
is_windows = False
|
|
if os_name != "linux" and os_name != "darwin":
|
|
# It's possible we're using MSYS/Cygwin/MinGW, treat them all as Windows
|
|
is_windows = True
|
|
os_name = "windows"
|
|
EXE_EXT = ".exe" if is_windows else ""
|
|
|
|
no_color = False
|
|
if is_windows:
|
|
try:
|
|
import colorama
|
|
|
|
colorama.init()
|
|
except ImportError:
|
|
# We can't do ANSI color codes in terminal on Windows without colorama
|
|
no_color = True
|
|
|
|
# Environment detection
|
|
if not sys.version_info >= (3, 8):
|
|
error("Requires Python 3.8+")
|
|
|
|
cpu_count = multiprocessing.cpu_count()
|
|
|
|
# When directly invoked, make it a command wrapper
|
|
if __name__ == "__main__":
|
|
ensure_cargo()
|
|
ensure_jdk()
|
|
subprocess.run(sys.argv[1:])
|