Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Has been cancelled
Build Docs / Deploy Docs (push) Has been cancelled
Windows CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

123 lines
3.8 KiB
Python

"""The build script for mlc4j (MLC LLM and tvm4j)"""
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
from mlc_llm.support import logging
logging.enable_logging()
logger = logging.getLogger(__name__)
def run_cmake(mlc4j_path: Path):
if "ANDROID_NDK" not in os.environ:
raise ValueError(
'Environment variable "ANDROID_NDK" is required but not found.'
"Please follow https://llm.mlc.ai/docs/deploy/android.html to properly "
'specify "ANDROID_NDK".'
)
logger.info("Running cmake")
# use pathlib so it is cross platform
android_ndk_path = (
Path(os.environ["ANDROID_NDK"]) / "build" / "cmake" / "android.toolchain.cmake"
)
cmd = [
"cmake",
str(mlc4j_path),
"-DCMAKE_BUILD_TYPE=Release",
f"-DCMAKE_TOOLCHAIN_FILE={str(android_ndk_path)}",
"-DCMAKE_INSTALL_PREFIX=.",
'-DCMAKE_CXX_FLAGS="-O3"',
"-DANDROID_ABI=arm64-v8a",
"-DANDROID_NATIVE_API_LEVEL=android-24",
"-DANDROID_PLATFORM=android-24",
"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON",
"-DANDROID_STL=c++_static",
"-DUSE_HEXAGON_SDK=OFF",
"-DMLC_LLM_INSTALL_STATIC_LIB=ON",
"-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON",
"-DUSE_OPENCL=ON",
"-DUSE_OPENCL_ENABLE_HOST_PTR=ON",
"-DUSE_CUSTOM_LOGGING=ON",
"-DTVM_FFI_USE_LIBBACKTRACE=OFF",
"-DTVM_FFI_BACKTRACE_ON_SEGFAULT=OFF",
]
if sys.platform == "win32":
logger.info("Using ninja in windows, make sure you installed ninja in conda")
cmd += ["-G", "Ninja"]
subprocess.run(cmd, check=True, env=os.environ)
def run_cmake_build():
logger.info("Running cmake build")
cmd = [
"cmake",
"--build",
".",
"--target",
"tvm4j_runtime_packed",
"--config",
"release",
f"-j{os.cpu_count()}",
]
subprocess.run(cmd, check=True, env=os.environ)
def run_cmake_install():
logger.info("Running cmake install")
cmd = [
"cmake",
"--build",
".",
"--target",
"install",
"--config",
"release",
f"-j{os.cpu_count()}",
]
subprocess.run(cmd, check=True, env=os.environ)
def main(mlc_llm_source_dir: Path):
# - Setup rust.
subprocess.run(["rustup", "target", "add", "aarch64-linux-android"], check=True, env=os.environ)
# - Build MLC LLM and tvm4j.
build_path = Path("build")
os.makedirs(build_path / "lib", exist_ok=True)
logger.info('Entering "%s" for MLC LLM and tvm4j build.', os.path.abspath(build_path))
os.chdir(build_path)
# Generate config.cmake if TVM Home is set.
if "TVM_SOURCE_DIR" in os.environ:
logger.info('Set TVM_SOURCE_DIR to "%s"', os.environ["TVM_SOURCE_DIR"])
with open("config.cmake", "w", encoding="utf-8") as file:
# We use "json.dumps" to escape backslashes and quotation marks
tvm_source_dir_str_with_escape = json.dumps(os.environ["TVM_SOURCE_DIR"])
print("set(TVM_SOURCE_DIR %s)" % tvm_source_dir_str_with_escape, file=file)
# - Run cmake, build and install
run_cmake(mlc_llm_source_dir / "android" / "mlc4j")
run_cmake_build()
run_cmake_install()
if __name__ == "__main__":
parser = argparse.ArgumentParser("MLC LLM Android Lib Preparation")
parser.add_argument(
"--mlc-llm-source-dir",
type=Path,
default=os.environ.get("MLC_LLM_SOURCE_DIR", None),
help="The path to MLC LLM source",
)
parsed = parser.parse_args()
if parsed.mlc_llm_source_dir is None:
parsed.mlc_llm_source_dir = Path(os.path.abspath(os.path.curdir)).parent.parent
os.environ["MLC_LLM_SOURCE_DIR"] = str(parsed.mlc_llm_source_dir)
main(parsed.mlc_llm_source_dir)