| # Copyright 2024 The Servo Project Developers. See the COPYRIGHT |
| # file at the top-level directory of this distribution. |
| # |
| # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| # option. This file may not be copied, modified, or distributed |
| # except according to those terms. |
| |
| from typing import TypeGuard |
| import errno |
| import json |
| import os |
| import pathlib |
| import platform |
| import shutil |
| import subprocess |
| import sys |
| from enum import Enum |
| |
| from os import path |
| from packaging.version import parse as parse_version |
| from typing import Any, Optional |
| |
| import servo.platform |
| import servo.util as util |
| |
| |
| class SanitizerKind(Enum): |
| NONE = 0 |
| ASAN = 1 |
| TSAN = 2 |
| |
| # Apparently enums don't always compare across modules, so we define |
| # helper methods. |
| def is_asan(self) -> bool: |
| return self is self.ASAN |
| |
| def is_tsan(self) -> bool: |
| return self is self.TSAN |
| |
| # Returns true if no sanitizer is enabled. |
| def is_none(self) -> bool: |
| return self is self.NONE |
| |
| # Returns true if a sanitizer is enabled. |
| def is_some(self) -> bool: |
| return not self.is_none() |
| |
| |
| class BuildTarget(object): |
| def __init__(self, target_triple: str) -> None: |
| self.target_triple = target_triple |
| |
| @staticmethod |
| def from_triple(target_triple: Optional[str]) -> "BuildTarget": |
| host_triple = servo.platform.host_triple() |
| if target_triple: |
| if "android" in target_triple: |
| return AndroidTarget(target_triple) |
| elif "ohos" in target_triple: |
| return OpenHarmonyTarget(target_triple) |
| elif target_triple != host_triple: |
| raise Exception(f"Unknown build target {target_triple}") |
| return BuildTarget(host_triple) |
| |
| def triple(self) -> str: |
| return self.target_triple |
| |
| def binary_name(self) -> str: |
| return f"servo{servo.platform.get().executable_suffix()}" |
| |
| def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None: |
| pass |
| |
| def is_cross_build(self) -> bool: |
| return False |
| |
| def needs_packaging(self) -> bool: |
| return False |
| |
| |
| class CrossBuildTarget(BuildTarget): |
| def is_cross_build(self) -> bool: |
| return True |
| |
| |
| class AndroidTarget(CrossBuildTarget): |
| DEFAULT_TRIPLE = "aarch64-linux-android" |
| |
| def ndk_configuration(self) -> dict[str, str]: |
| target = self.triple() |
| config = {} |
| if target == "armv7-linux-androideabi": |
| config["platform"] = "android-30" |
| config["target"] = target |
| config["toolchain_prefix"] = "arm-linux-androideabi" |
| config["arch"] = "arm" |
| config["lib"] = "armeabi-v7a" |
| config["toolchain_name"] = "armv7a-linux-androideabi30" |
| elif target == "aarch64-linux-android": |
| config["platform"] = "android-30" |
| config["target"] = target |
| config["toolchain_prefix"] = target |
| config["arch"] = "arm64" |
| config["lib"] = "arm64-v8a" |
| config["toolchain_name"] = "aarch64-linux-androideabi30" |
| elif target == "i686-linux-android": |
| # https://github.com/jemalloc/jemalloc/issues/1279 |
| config["platform"] = "android-30" |
| config["target"] = target |
| config["toolchain_prefix"] = target |
| config["arch"] = "x86" |
| config["lib"] = "x86" |
| config["toolchain_name"] = "i686-linux-android30" |
| elif target == "x86_64-linux-android": |
| config["platform"] = "android-30" |
| config["target"] = target |
| config["toolchain_prefix"] = target |
| config["arch"] = "x86_64" |
| config["lib"] = "x86_64" |
| config["toolchain_name"] = "x86_64-linux-android30" |
| else: |
| raise Exception(f"Unknown android target {target}") |
| |
| return config |
| |
| def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None: |
| # Paths to Android build tools: |
| if config["android"]["sdk"]: |
| env["ANDROID_SDK_ROOT"] = config["android"]["sdk"] |
| if config["android"]["ndk"]: |
| env["ANDROID_NDK_ROOT"] = config["android"]["ndk"] |
| |
| toolchains = path.join(topdir, "android-toolchains") |
| for kind in ["sdk", "ndk"]: |
| default = os.path.join(toolchains, kind) |
| if os.path.isdir(default): |
| env.setdefault(f"ANDROID_{kind.upper()}_ROOT", default) |
| |
| if "IN_NIX_SHELL" in env and ("ANDROID_NDK_ROOT" not in env or "ANDROID_SDK_ROOT" not in env): |
| print("Please set SERVO_ANDROID_BUILD=1 when starting the Nix shell to include the Android SDK/NDK.") |
| sys.exit(1) |
| if "ANDROID_NDK_ROOT" not in env: |
| print("Please set the ANDROID_NDK_ROOT environment variable.") |
| sys.exit(1) |
| if "ANDROID_SDK_ROOT" not in env: |
| print("Please set the ANDROID_SDK_ROOT environment variable.") |
| sys.exit(1) |
| |
| ndk_configuration = self.ndk_configuration() |
| android_platform = ndk_configuration["platform"] |
| android_toolchain_name = ndk_configuration["toolchain_name"] |
| android_lib = ndk_configuration["lib"] |
| |
| android_api = android_platform.replace("android-", "") |
| |
| # Check if the NDK version is 28 |
| if not os.path.isfile(path.join(env["ANDROID_NDK_ROOT"], "source.properties")): |
| print("ANDROID_NDK should have file `source.properties`.") |
| print("The environment variable ANDROID_NDK_ROOT may be set at a wrong path.") |
| sys.exit(1) |
| with open(path.join(env["ANDROID_NDK_ROOT"], "source.properties"), encoding="utf8") as ndk_properties: |
| lines = ndk_properties.readlines() |
| if lines[1].split(" = ")[1].split(".")[0] != "28": |
| print("Servo currently only supports NDK r28.") |
| sys.exit(1) |
| |
| # Android builds also require having the gcc bits on the PATH and various INCLUDE |
| # path munging if you do not want to install a standalone NDK. See: |
| # https://dxr.mozilla.org/mozilla-central/source/build/autoconf/android.m4#139-161 |
| os_type = platform.system().lower() |
| if os_type not in ["linux", "darwin"]: |
| raise Exception("Android cross builds are only supported on Linux and macOS.") |
| |
| llvm_prebuilt = path.join(env["ANDROID_NDK_ROOT"], "toolchains", "llvm", "prebuilt") |
| |
| cpu_type = platform.machine().lower() |
| host_suffix = "unknown" |
| if cpu_type in ["i386", "i486", "i686", "i768", "x86"]: |
| host_suffix = "x86" |
| elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]: |
| host_suffix = "x86_64" |
| else: |
| available_prebuilts = os.listdir(llvm_prebuilt) |
| available_prebuilts = [prebuilt for prebuilt in available_prebuilts if prebuilt.startswith(os_type)] |
| # If there is only one prebuilt option available, it's probably the right one for the host |
| # platform. E.g. on Arm macs, only the x86 prebuilts are available, buts that perfectly fine, |
| # since there is rosetta. |
| if len(available_prebuilts) == 1: |
| host_suffix = available_prebuilts[0].removeprefix(f"{os_type}-") |
| else: |
| print(f"Error: Can't determine LLVM prebuilt. Unknown cpu type {cpu_type}.") |
| print(f"Hint: The LLVM prebuilts folder contains the following entries: {available_prebuilts}") |
| print("Please open an issue with the above information") |
| raise Exception("Can't determine LLVM prebuilt directory.") |
| host = os_type + "-" + host_suffix |
| |
| host_cc = env.get("HOST_CC") or shutil.which("clang") |
| host_cxx = env.get("HOST_CXX") or shutil.which("clang++") |
| |
| llvm_toolchain = path.join(llvm_prebuilt, host) |
| env["PATH"] = env["PATH"] + ":" + path.join(llvm_toolchain, "bin") |
| |
| def to_ndk_bin(prog: str) -> str: |
| return path.join(llvm_toolchain, "bin", prog) |
| |
| # This workaround is due to an issue in the x86_64 Android NDK that introduces |
| # an undefined reference to the symbol '__extendsftf2'. |
| # See https://github.com/termux/termux-packages/issues/8029#issuecomment-1369150244 |
| if "x86_64" in self.triple(): |
| libclangrt_filename = subprocess.run( |
| [to_ndk_bin(f"x86_64-linux-android{android_api}-clang"), "--print-libgcc-file-name"], |
| check=True, |
| capture_output=True, |
| encoding="utf8", |
| ).stdout |
| env["RUSTFLAGS"] = env.get("RUSTFLAGS", "") |
| env["RUSTFLAGS"] += f"-C link-arg={libclangrt_filename}" |
| |
| assert host_cc |
| assert host_cxx |
| |
| env["RUST_TARGET"] = self.triple() |
| env["HOST_CC"] = host_cc |
| env["HOST_CXX"] = host_cxx |
| env["HOST_CFLAGS"] = "" |
| env["HOST_CXXFLAGS"] = "" |
| env["TARGET_CC"] = to_ndk_bin("clang") |
| env["TARGET_CPP"] = to_ndk_bin("clang") + " -E" |
| env["TARGET_CXX"] = to_ndk_bin("clang++") |
| |
| env["TARGET_AR"] = to_ndk_bin("llvm-ar") |
| env["TARGET_RANLIB"] = to_ndk_bin("llvm-ranlib") |
| env["TARGET_OBJCOPY"] = to_ndk_bin("llvm-objcopy") |
| env["TARGET_YASM"] = to_ndk_bin("yasm") |
| env["TARGET_STRIP"] = to_ndk_bin("llvm-strip") |
| env["RUST_FONTCONFIG_DLOPEN"] = "on" |
| |
| env["LIBCLANG_PATH"] = path.join(llvm_toolchain, "lib") |
| env["CLANG_PATH"] = to_ndk_bin("clang") |
| env["BINDGEN_EXTRA_CLANG_ARGS"] = ( |
| f"--target={android_toolchain_name} --sysroot={path.join(llvm_toolchain, 'sysroot')}" |
| ) |
| |
| # A cheat-sheet for some of the build errors caused by getting the search path wrong... |
| # |
| # fatal error: 'limits' file not found |
| # -- add -I cxx_include |
| # unknown type name '__locale_t' (when running bindgen in mozjs_sys) |
| # -- add -isystem sysroot_include |
| # error: use of undeclared identifier 'UINTMAX_C' |
| # -- add -D__STDC_CONSTANT_MACROS |
| # |
| # Also worth remembering: autoconf uses C for its configuration, |
| # even for C++ builds, so the C flags need to line up with the C++ flags. |
| env["TARGET_CFLAGS"] = env.get("TARGET_CFLAGS", "") + " " + "--target=" + android_toolchain_name |
| env["TARGET_CXXFLAGS"] = env.get("TARGET_CXXFLAGS", "") + " " + "--target=" + android_toolchain_name |
| |
| # These two variables are needed for the mozjs compilation. |
| env["ANDROID_API_LEVEL"] = android_api |
| env["ANDROID_NDK_HOME"] = env["ANDROID_NDK_ROOT"] |
| |
| # The two variables set below are passed by our custom |
| # support/android/toolchain.cmake to the NDK's CMake toolchain file |
| env["ANDROID_ABI"] = android_lib |
| env["ANDROID_PLATFORM"] = android_platform |
| env["NDK_CMAKE_TOOLCHAIN_FILE"] = path.join( |
| env["ANDROID_NDK_ROOT"], "build", "cmake", "android.toolchain.cmake" |
| ) |
| env["CMAKE_TOOLCHAIN_FILE"] = path.join(topdir, "support", "android", "toolchain.cmake") |
| |
| # Set output dir for gradle aar files |
| env["AAR_OUT_DIR"] = path.join(topdir, "target", "android", "aar") |
| if not os.path.exists(env["AAR_OUT_DIR"]): |
| os.makedirs(env["AAR_OUT_DIR"]) |
| |
| env["TARGET_PKG_CONFIG_SYSROOT_DIR"] = path.join(llvm_toolchain, "sysroot") |
| |
| def binary_name(self) -> str: |
| return "libservoshell.so" |
| |
| def is_cross_build(self) -> bool: |
| return True |
| |
| def needs_packaging(self) -> bool: |
| return True |
| |
| def get_package_path(self, build_type_directory: str) -> str: |
| base_path = util.get_target_dir() |
| base_path = path.join(base_path, "android", self.triple()) |
| apk_name = "servoapp.apk" |
| return path.join(base_path, build_type_directory, apk_name) |
| |
| |
| class OpenHarmonyTarget(CrossBuildTarget): |
| DEFAULT_TRIPLE = "aarch64-unknown-linux-ohos" |
| |
| def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None: |
| # Paths to OpenHarmony SDK and build tools: |
| # Note: `OHOS_SDK_NATIVE` is the CMake variable name the `hvigor` build-system |
| # uses for the native directory of the SDK, so we use the same name to be consistent. |
| if "OHOS_SDK_NATIVE" not in env and config["ohos"]["ndk"]: |
| env["OHOS_SDK_NATIVE"] = config["ohos"]["ndk"] |
| |
| if "OHOS_SDK_NATIVE" not in env: |
| print( |
| "Please set the OHOS_SDK_NATIVE environment variable to the location of the `native` directory " |
| "in the OpenHarmony SDK." |
| ) |
| sys.exit(1) |
| |
| ndk_root = pathlib.Path(env["OHOS_SDK_NATIVE"]) |
| |
| if not ndk_root.is_dir(): |
| print(f"OHOS_SDK_NATIVE is not set to a valid directory: `{ndk_root}`") |
| sys.exit(1) |
| |
| ndk_root = ndk_root.resolve() |
| package_info = ndk_root.joinpath("oh-uni-package.json") |
| try: |
| with open(package_info) as meta_file: |
| meta = json.load(meta_file) |
| ohos_api_version = int(meta["apiVersion"]) |
| ohos_sdk_version = parse_version(meta["version"]) |
| if ohos_sdk_version < parse_version("5.0") or ohos_api_version < 14: |
| raise RuntimeError("Building servo for OpenHarmony requires SDK version 5.0.2 (API-14) or newer.") |
| print(f"Info: The OpenHarmony SDK {ohos_sdk_version} is targeting API-level {ohos_api_version}") |
| except (OSError, json.JSONDecodeError) as e: |
| print(f"Failed to read metadata information from {package_info}") |
| print(f"Exception: {e}") |
| |
| llvm_toolchain = ndk_root.joinpath("llvm") |
| llvm_bin = llvm_toolchain.joinpath("bin") |
| ohos_sysroot = ndk_root.joinpath("sysroot") |
| if not (llvm_toolchain.is_dir() and llvm_bin.is_dir()): |
| print(f"Expected to find `llvm` and `llvm/bin` folder under $OHOS_SDK_NATIVE at `{llvm_toolchain}`") |
| sys.exit(1) |
| if not ohos_sysroot.is_dir(): |
| print(f"Could not find OpenHarmony sysroot in {ndk_root}") |
| sys.exit(1) |
| # When passing the sysroot to Rust crates such as `cc-rs` or bindgen, we should pass |
| # POSIX paths, since otherwise the backslashes in windows paths may be interpreted as |
| # escapes and lead to errors. |
| ohos_sysroot_posix = ohos_sysroot.as_posix() |
| |
| # Note: We don't use the `<target_triple>-clang` wrappers on purpose, since |
| # a) the OH 4.0 SDK does not have them yet AND |
| # b) the wrappers in the newer SDKs are bash scripts, which can cause problems |
| # on windows, depending on how the wrapper is called. |
| # Instead, we ensure that all the necessary flags for the c-compiler are set |
| # via environment variables such as `TARGET_CFLAGS`. |
| def to_sdk_llvm_bin(prog: str) -> str: |
| if sys.platform == "win32": |
| prog = prog + ".exe" |
| llvm_prog = llvm_bin.joinpath(prog) |
| if not llvm_prog.is_file(): |
| raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), llvm_prog) |
| return llvm_bin.joinpath(prog).as_posix() |
| |
| # CC and CXX should already be set to appropriate host compilers by `build_env()` |
| env["HOST_CC"] = env["CC"] |
| env["HOST_CXX"] = env["CXX"] |
| env["TARGET_AR"] = to_sdk_llvm_bin("llvm-ar") |
| env["TARGET_RANLIB"] = to_sdk_llvm_bin("llvm-ranlib") |
| env["TARGET_READELF"] = to_sdk_llvm_bin("llvm-readelf") |
| env["TARGET_OBJCOPY"] = to_sdk_llvm_bin("llvm-objcopy") |
| env["TARGET_STRIP"] = to_sdk_llvm_bin("llvm-strip") |
| |
| target_triple = self.triple() |
| rust_target_triple = str(target_triple).replace("-", "_") |
| ndk_clang = to_sdk_llvm_bin("clang") |
| ndk_clangxx = to_sdk_llvm_bin("clang++") |
| env[f"CC_{rust_target_triple}"] = ndk_clang |
| env[f"CXX_{rust_target_triple}"] = ndk_clangxx |
| # The clang target name is different from the LLVM target name |
| clang_target_triple = str(target_triple).replace("-unknown-", "-") |
| clang_target_triple_underscore = clang_target_triple.replace("-", "_") |
| env[f"CC_{clang_target_triple_underscore}"] = ndk_clang |
| env[f"CXX_{clang_target_triple_underscore}"] = ndk_clangxx |
| # rustc linker |
| env[f"CARGO_TARGET_{rust_target_triple.upper()}_LINKER"] = ndk_clang |
| |
| link_args = [ |
| "-fuse-ld=lld", |
| f"--target={clang_target_triple}", |
| f"--sysroot={ohos_sysroot_posix}", |
| "-Wl,--build-id", |
| ] |
| |
| env["HOST_CFLAGS"] = "" |
| env["HOST_CXXFLAGS"] = "" |
| ohos_cflags = [ |
| "-D__MUSL__", |
| "-DMDB_USE_ROBUST=0", |
| f" --target={clang_target_triple}", |
| f" --sysroot={ohos_sysroot_posix}", |
| "-Wno-error=unused-command-line-argument", |
| ] |
| if clang_target_triple.startswith("armv7-"): |
| ohos_cflags.extend(["-march=armv7-a", "-mfloat-abi=softfp", "-mtune=generic-armv7-a", "-mthumb"]) |
| ohos_cflags_str = " ".join(ohos_cflags) |
| |
| env["TARGET_CFLAGS"] = env.get("TARGET_CFLAGS", "") + " " + ohos_cflags_str |
| env["TARGET_CPPFLAGS"] = env.get("TARGET_CPPFLAGS", "") + " " + "-D__MUSL__" |
| env["TARGET_CXXFLAGS"] = env.get("TARGET_CXXFLAGS", "") + " " + ohos_cflags_str |
| |
| # CMake related flags |
| env["CMAKE"] = ndk_root.joinpath("build-tools", "cmake", "bin", "cmake").as_posix() |
| cmake_toolchain_file = ndk_root.joinpath("build", "cmake", "ohos.toolchain.cmake") |
| if cmake_toolchain_file.is_file(): |
| env[f"CMAKE_TOOLCHAIN_FILE_{rust_target_triple}"] = cmake_toolchain_file.as_posix() |
| else: |
| print( |
| f"Warning: Failed to find the OpenHarmony CMake Toolchain file - Expected it at {cmake_toolchain_file}" |
| ) |
| env[f"CMAKE_C_COMPILER_{rust_target_triple}"] = ndk_clang |
| env[f"CMAKE_CXX_COMPILER_{rust_target_triple}"] = ndk_clangxx |
| |
| # pkg-config |
| pkg_config_path = "{}:{}".format( |
| ohos_sysroot.joinpath("usr", "lib", "pkgconfig").as_posix(), |
| ohos_sysroot.joinpath("usr", "share", "pkgconfig").as_posix(), |
| ) |
| env[f"PKG_CONFIG_SYSROOT_DIR_{rust_target_triple}"] = ohos_sysroot_posix |
| env[f"PKG_CONFIG_PATH_{rust_target_triple}"] = pkg_config_path |
| |
| # bindgen / libclang-sys |
| env["LIBCLANG_PATH"] = path.join(llvm_toolchain, "lib") |
| env["CLANG_PATH"] = ndk_clangxx |
| env[f"CXXSTDLIB_{clang_target_triple_underscore}"] = "c++" |
| bindgen_extra_clangs_args_var = f"BINDGEN_EXTRA_CLANG_ARGS_{rust_target_triple}" |
| bindgen_extra_clangs_args = env.get(bindgen_extra_clangs_args_var, "") |
| bindgen_extra_clangs_args = bindgen_extra_clangs_args + " " + ohos_cflags_str |
| env[bindgen_extra_clangs_args_var] = bindgen_extra_clangs_args |
| |
| sanitizer: SanitizerKind = config["build"]["sanitizer"] |
| san_compile_flags = [] |
| if sanitizer.is_some(): |
| # Lookup `<sdk>/native/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos/libclang_rt.asan.so` |
| lib_clang = llvm_toolchain.joinpath("lib", "clang") |
| children = [f.path for f in os.scandir(lib_clang) if f.is_dir()] |
| if len(children) != 1: |
| raise RuntimeError(f"Expected exactly 1 libclang version: `{children}`") |
| lib_clang_version_dir = pathlib.Path(children[0]) |
| libclang_arch = lib_clang_version_dir.joinpath("lib", clang_target_triple).resolve() |
| # Use the clangrt from the NDK to use the same library for both C++ and Rust. |
| env["RUSTFLAGS"] += " -Zexternal-clangrt" |
| san_compile_flags.append("-fno-omit-frame-pointer") |
| |
| # On OpenHarmony we add some additional flags when asan is enabled |
| if sanitizer.is_asan(): |
| libasan_so_path = libclang_arch.joinpath("libclang_rt.asan.so") |
| libasan_preinit_path = libclang_arch.joinpath("libclang_rt.asan-preinit.a") |
| if not libasan_so_path.exists(): |
| raise RuntimeError(f"Couldn't find ASAN runtime library at {libasan_so_path}") |
| link_args.extend( |
| [ |
| "-fsanitize=address", |
| "--rtlib=compiler-rt", |
| "-shared-libasan", |
| str(libasan_so_path), |
| "-Wl,--whole-archive", |
| "-Wl," + str(libasan_preinit_path), |
| "-Wl,--no-whole-archive", |
| ] |
| ) |
| |
| san_compile_flags.extend(["-fsanitize=address", "-shared-libasan", "-fsanitize-recover=address"]) |
| |
| arch_asan_ignore_list = lib_clang_version_dir.joinpath("share", "asan_ignorelist.txt") |
| if arch_asan_ignore_list.exists(): |
| san_compile_flags.append("-fsanitize-system-ignorelist=" + str(arch_asan_ignore_list)) |
| else: |
| print(f"Warning: Couldn't find system ASAN ignorelist at `{arch_asan_ignore_list}`") |
| elif sanitizer.is_tsan(): |
| libtsan_so_path = libclang_arch.joinpath("libclang_rt.tsan.so") |
| builtins_path = libclang_arch.joinpath("libclang_rt.builtins.a") |
| |
| link_args.extend( |
| ["-fsanitize=thread", "--rtlib=compiler-rt", "-shared-libsan", str(libtsan_so_path), str(builtins_path)] |
| ) |
| san_compile_flags.append("-shared-libsan") |
| |
| link_args = [f"-Clink-arg={arg}" for arg in link_args] |
| env["RUSTFLAGS"] += " " + " ".join(link_args) |
| env["TARGET_CFLAGS"] += " " + " ".join(san_compile_flags) |
| env["TARGET_CXXFLAGS"] += " " + " ".join(san_compile_flags) |
| |
| def binary_name(self) -> str: |
| return "libservoshell.so" |
| |
| def needs_packaging(self) -> bool: |
| return True |
| |
| def get_package_path(self, build_type_directory: str, flavor: Optional[str] = None) -> str: |
| base_path = util.get_target_dir() |
| base_path = path.join(base_path, "openharmony", self.triple()) |
| hap_name = "servoshell-default-signed.hap" |
| if not flavor: |
| flavor = "default" |
| build_output_path = path.join("entry", "build", flavor, "outputs", "default") |
| return path.join(base_path, build_type_directory, build_output_path, hap_name) |
| |
| def abi_string(self) -> str: |
| abi_map = {"aarch64-unknown-linux-ohos": "arm64-v8a", "x86_64-unknown-linux-ohos": "x86_64"} |
| return abi_map[self.triple()] |
| |
| |
| def is_android(target: BuildTarget) -> TypeGuard[AndroidTarget]: |
| return isinstance(target, AndroidTarget) |
| |
| |
| def is_openharmony(target: BuildTarget) -> TypeGuard[OpenHarmonyTarget]: |
| return isinstance(target, OpenHarmonyTarget) |