"""cangjie compiler build entry"""
import argparse
import logging
import multiprocessing
import os
import platform
import re
import tarfile
import shutil
import stat
import subprocess
import sys
from enum import Enum
from logging.handlers import TimedRotatingFileHandler
from pathlib import Path
from subprocess import DEVNULL, PIPE
HOME_DIR = os.path.dirname(os.path.abspath(__file__))
BUILD_DIR = os.path.join(HOME_DIR, "build")
CMAKE_BUILD_DIR = os.path.join(BUILD_DIR, "build")
CMAKE_OUTPUT_DIR = os.path.join(HOME_DIR, "output")
OUTPUT_BIN_DIR = os.path.join(CMAKE_OUTPUT_DIR, "bin")
LOG_DIR = os.path.join(BUILD_DIR, "logs")
LOG_FILE = os.path.join(LOG_DIR, "cangjie.log")
IS_WINDOWS = platform.system() == "Windows"
IS_MACOS = platform.system() == "Darwin"
IS_ARM = platform.uname().processor in ["aarch64", "arm", "arm64"]
CJ_OS_NAME = platform.system().lower()
CJ_ARCH_NAME = platform.machine().replace("AMD64", "x86_64").lower()
CAN_RUN_CJNATIVE = (platform.system(), platform.machine()) in [
("Linux", "x86_64"),
("Linux", "aarch64"),
("Windows", "AMD64"),
("Darwin", "x86_64"),
("Darwin", "arm64")
]
LD_LIBRARY_PATH = "Path" if IS_WINDOWS else "DYLD_LIBRARY_PATH" if IS_MACOS else "LD_LIBRARY_PATH"
LIBRARY_PATH = "Path" if IS_WINDOWS else "LIBRARY_PATH"
PATH_ENV_SEPERATOR = ";" if IS_WINDOWS else ":"
DELTA_JOBS = 2
MAKE_JOBS = multiprocessing.cpu_count() + DELTA_JOBS
PYTHON_EXECUTABLE = sys.executable
TARGET_DICTIONARY = {
"native": None,
"ohos-aarch64": "aarch64-linux-ohos",
"ohos-arm": "arm-linux-ohos",
"ohos-x86_64": "x86_64-linux-ohos",
"windows-x86_64": "x86_64-w64-mingw32",
"ios-simulator-aarch64": "arm64-apple-ios11-simulator",
"ios-simulator-x86_64": "x86_64-apple-ios11-simulator",
"ios-aarch64": "arm64-apple-ios11",
"android-aarch64": "aarch64-linux-android31",
"android31-aarch64": "aarch64-linux-android31",
"android26-aarch64": "aarch64-linux-android26",
"android23-arm": "arm-linux-android23",
"android-x86_64": "x86_64-linux-android",
"android31-x86_64": "x86_64-linux-android31",
"android26-x86_64": "x86_64-linux-android26"
}
def resolve_path(path):
if os.path.isabs(path):
return path
return os.path.abspath(path)
def log_output(output, checker=None, args=[]):
"""log command output"""
while True:
line = output.stdout.readline()
if not line:
output.communicate()
returncode = output.returncode
if returncode != 0:
LOG.error("build error: %d!\n", returncode)
sys.exit(1)
break
try:
LOG.info(line.decode("ascii", "ignore").rstrip())
except UnicodeEncodeError:
LOG.info(line.decode("utf-8", "ignore").rstrip())
def generate_version_tail(target):
"""generate version string for cjc compiler"""
res = "{}".format("cjnative")
res = " (" + res + ")"
arch_name = platform.machine().replace("AMD64", "x86_64").replace("arm64", "aarch64").lower()
vendor_name = "apple" if IS_MACOS else "unknown"
os_name = None
if IS_WINDOWS:
os_name = "windows-gnu"
elif IS_MACOS:
os_name = "darwin"
else:
os_name = "linux-gnu"
res += "\\nTarget: {}".format(target) if target else "\\nTarget: {0}-{1}-{2}".format(arch_name, vendor_name, os_name)
return str(res)
def generate_cmake_defs(args):
"""convert args to cmake defs"""
def bool_to_opt(value):
return "ON" if value else "OFF"
if args.target:
if args.target == "aarch64-linux-ohos":
toolchain_file = "ohos_aarch64_clang_toolchain.cmake"
elif args.target == "arm-linux-ohos":
toolchain_file = "ohos_arm_clang_toolchain.cmake"
elif args.target == "x86_64-linux-ohos":
toolchain_file = "ohos_x86_64_clang_toolchain.cmake"
elif args.target == "x86_64-w64-mingw32":
toolchain_file = "mingw_x86_64_toolchain.cmake"
elif args.target == "arm64-apple-ios11-simulator":
toolchain_file = "ios_simulator_arm64_toolchain.cmake"
elif args.target == "x86_64-apple-ios11-simulator":
toolchain_file = "ios_simulator_x86_64_toolchain.cmake"
elif args.target == "arm64-apple-ios11":
toolchain_file = "ios_arm64_toolchain.cmake"
elif args.target == "arm-linux-android23":
toolchain_file = "android_arm32_toolchain.cmake"
elif "aarch64-linux-android" in args.target:
toolchain_file = "android_aarch64_toolchain.cmake"
elif "x86_64-linux-android" in args.target:
toolchain_file = "android_x86_64_toolchain.cmake"
else:
args.target = None
if IS_WINDOWS:
toolchain_file = "mingw_x86_64_toolchain.cmake"
elif IS_MACOS:
toolchain_file = "darwin_aarch64_toolchain.cmake" if IS_ARM else "darwin_x86_64_toolchain.cmake"
elif IS_ARM:
toolchain_file = "linux_aarch64_toolchain.cmake"
else:
toolchain_file = "linux_x86_64_toolchain.cmake"
install_prefix = CMAKE_OUTPUT_DIR + ("-{}".format(args.target) if (args.target and args.product == "cjc") else "")
result = [
"-DCMAKE_BUILD_TYPE=" + args.build_type.value,
"-DCMAKE_ENABLE_ASSERT=" + bool_to_opt(args.enable_assert),
"-DCMAKE_INSTALL_PREFIX=" + install_prefix,
"-DCMAKE_TOOLCHAIN_FILE=" + os.path.join(HOME_DIR, "cmake/" + toolchain_file),
"-DCMAKE_PREFIX_PATH=" + (args.target_toolchain + "/../x86_64-w64-mingw32" if args.target_toolchain else ""),
"-DCANGJIE_BUILD_TESTS=" + bool_to_opt((not args.no_tests) and (args.product == "all" or args.product == "libs")),
"-DCANGJIE_BUILD_CJC=" + bool_to_opt(args.product in ['all', 'cjc']),
"-DCANGJIE_BUILD_STD_SUPPORT=" + bool_to_opt(args.product in ['all', 'libs']),
"-DCANGJIE_BUILD_CJDB=" + bool_to_opt(args.build_cjdb),
"-DCANGJIE_BUILD_CJDB_DISABLE_PYTHON=" + bool_to_opt(args.cjdb_disable_python),
"-DCANGJIE_INSTALL_CJDB_SCRIPT=" + bool_to_opt(args.install_cjdb_script),
"-DCANGJIE_ENABLE_HWASAN=" + bool_to_opt(args.hwasan),
"-DCANGJIE_VERSION=" + generate_version_tail(args.target),
"-DBUILD_GCC_TOOLCHAIN=" + (args.gcc_toolchain if args.gcc_toolchain and args.target is None else ""),
"-DCANGJIE_TARGET_LIB=" + (";".join(args.target_lib) if args.target_lib else ""),
"-DCANGJIE_TARGET_TOOLCHAIN=" + (args.target_toolchain if args.target_toolchain else ""),
"-DCANGJIE_INCLUDE=" + (";".join(args.include) if args.include else ""),
"-DCANGJIE_TARGET_SYSROOT=" + (args.target_sysroot if args.target_sysroot else ""),
"-DCANGJIE_LINK_JOB_POOL=" + (str(args.link_jobs) if args.link_jobs != 0 else ""),
"-DCANGJIE_DISABLE_STACK_GROW_FEATURE=" + bool_to_opt(args.disable_stack_grow_feature),
"-DCANGJIE_USE_OH_LLVM_REPO=" + bool_to_opt(args.use_oh_llvm_repo),
"-DCANGJIE_ENABLE_SANITIZE_OPTION=" + bool_to_opt(args.enable_sanitize_option)]
if args.version:
result.append("-DCJ_SDK_VERSION=" + args.version)
if args.target and "aarch64-linux-android" in args.target:
android_api_level = re.match(r'aarch64-linux-android(\d{2})?', args.target).group(1)
result.append("-DCMAKE_ANDROID_NDK=" + (args.android_ndk if args.android_ndk else ""))
result.append("-DCMAKE_ANDROID_API=" + (android_api_level if android_api_level else ""))
if args.sanitizer_support:
result.append("-DCANGJIE_SANITIZER_SUPPORT=" + args.sanitizer_support)
return result
def download_and_patch_tinytoml():
"""Set up the tinytoml third-party library"""
TINYTOML_DIR = os.path.join(HOME_DIR, "third_party", "tinytoml")
PATCH_FILE = os.path.join(TINYTOML_DIR, "fixFloatEqualError.patch")
TAR_PATH = os.path.join(TINYTOML_DIR, "tinytoml-0.4.tar.gz")
TINYTOMLTAR_PATH = os.path.join(TINYTOML_DIR, "tinytoml-0.4")
LOG.info("Setting up tinytoml...")
os.makedirs(os.path.dirname(TINYTOML_DIR), exist_ok=True)
try:
if not os.path.exists(TAR_PATH):
subprocess.run(
["git", "clone", "https://gitcode.com/src-openeuler/tinytoml.git",
TINYTOML_DIR],
check=True
)
subprocess.run(
["git", "checkout", "openEuler-24.03-LTS-SP1"],
cwd=TINYTOML_DIR,
check=True
)
LOG.info("Extracting tinytoml package...")
with tarfile.open(TAR_PATH) as tar:
tar.extractall(path=TINYTOML_DIR)
patch_file_exists = os.path.exists(PATCH_FILE)
target_dir_exists = os.path.exists(TINYTOMLTAR_PATH) and os.path.isdir(TINYTOMLTAR_PATH)
if patch_file_exists and target_dir_exists:
LOG.info("Applying tinytoml patch...")
subprocess.run(
f"patch -p0 -l -f < {PATCH_FILE}",
cwd=TINYTOMLTAR_PATH,
shell=True,
check=True
)
else:
LOG.warning(f"Patch file not found: {PATCH_FILE}")
TOML_H_SRC = os.path.join(TINYTOMLTAR_PATH, "include", "toml", "toml.h")
TOML_H_DST = os.path.join(TINYTOML_DIR, "toml.h")
if os.path.exists(TOML_H_SRC):
LOG.info(f"Copying toml.h from {TOML_H_SRC} to {TOML_H_DST}")
shutil.copy2(TOML_H_SRC, TOML_H_DST)
except subprocess.CalledProcessError as e:
LOG.error(f"Failed to setup tinytoml: {str(e)}")
if os.path.exists(TINYTOML_DIR):
shutil.rmtree(TINYTOML_DIR)
raise
def build(args):
if args.target:
args.target = TARGET_DICTIONARY[args.target]
if args.gcc_toolchain and args.target and args.product != "cjc":
LOG.warning("There is no intermediate or product targeting the host platform in this build, so --gcc-toolchain won't take effect")
tinytoml_target = os.path.join(HOME_DIR, "third_party", "tinytoml", "toml.h")
if not os.path.exists(tinytoml_target):
download_and_patch_tinytoml()
check_compiler(args)
"""build cangjie compiler"""
LOG.info("begin build...")
set_cjnative_backend_env(args)
if args.product is None:
args.product = "all" if args.target is None else "libs"
if args.target == "aarch64-linux-ohos" or args.target == "x86_64-linux-ohos":
os.environ["OHOS_ROOT"] = os.path.join(args.target_toolchain, "../../../../../..")
if args.android_ndk:
os.environ["ANDROID_NDK_ROOT"] = args.android_ndk
if IS_WINDOWS:
ninja_valid = True
try:
output = subprocess.Popen(["ninja", "--version"], stdout=DEVNULL, stderr=DEVNULL)
output.communicate()
ninja_valid = output.returncode == 0
except:
ninja_valid = False
if ninja_valid:
generator = "Ninja"
build_cmd = ["ninja"]
if args.jobs > 0:
build_cmd.extend(["-j", str(args.jobs)])
else:
generator = "MinGW Makefiles"
build_cmd = ["mingw32-make", "-j" + str(MAKE_JOBS)]
else:
generator = "Ninja"
build_cmd = ["ninja"]
if args.jobs > 0:
build_cmd.extend(["-j", str(args.jobs)])
cmake_command = ["cmake", HOME_DIR, "-G", generator] + generate_cmake_defs(args)
if args.print_cmd:
print(' '.join(cmake_command))
exit(0)
if args.target == "x86_64-w64-mingw32":
package_mingw_dependencies(args)
if not os.path.exists(BUILD_DIR):
os.makedirs(BUILD_DIR)
if args.sanitizer_support:
cmake_build_dir = os.path.join(BUILD_DIR, "build-libs-{}".format(args.sanitizer_support))
if args.target:
cmake_build_dir += "-{}".format(args.target)
else:
cmake_build_dir = os.path.join(BUILD_DIR, "build-{}-{}".format(args.product, args.target)) if args.target else CMAKE_BUILD_DIR
if not os.path.exists(os.path.join(cmake_build_dir, "build.ninja")):
os.makedirs(cmake_build_dir, exist_ok=True)
output = subprocess.Popen(cmake_command, cwd=cmake_build_dir, stdout=PIPE)
log_output(output)
output = subprocess.Popen(build_cmd, cwd=cmake_build_dir, stdout=PIPE)
log_output(output)
if output.returncode != 0:
LOG.fatal("build failed")
LOG.info("end build")
def set_cangjie_env(args):
os.environ["HOME_DIR"] = HOME_DIR
os.environ[LD_LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([
os.path.join(HOME_DIR, "output/lib"),
os.path.join(HOME_DIR, "output/runtime/lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)),
os.path.join(HOME_DIR, "output/tools/lib"),
os.environ.get(LD_LIBRARY_PATH, "")
])
LOG.info("set cangjie env: %s=%s", LD_LIBRARY_PATH, os.environ[LD_LIBRARY_PATH])
def set_cjnative_backend_env(args):
os.environ[LD_LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([
os.path.join(HOME_DIR, CMAKE_OUTPUT_DIR, "lib"),
os.path.join(HOME_DIR, CMAKE_OUTPUT_DIR, "lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)),
os.path.join(HOME_DIR, "build/build/lib"),
os.path.join(HOME_DIR, "build/build/lib/{os}_{arch}_cjnative".format(os=CJ_OS_NAME, arch=CJ_ARCH_NAME)),
os.environ.get(LD_LIBRARY_PATH, "")
] + (args.target_lib if hasattr(args, 'target_lib') else []))
LOG.info("set cjnative backend env: %s=%s", LD_LIBRARY_PATH, os.environ[LD_LIBRARY_PATH])
os.environ[LIBRARY_PATH] = PATH_ENV_SEPERATOR.join([
os.path.join(HOME_DIR, "build/build/lib"),
os.environ.get(LIBRARY_PATH, "")
] + (args.target_lib if hasattr(args, 'target_lib') else []))
LOG.info("set cjnative backend env: %s=%s", LIBRARY_PATH, os.environ[LIBRARY_PATH])
os.environ["PATH"] = PATH_ENV_SEPERATOR.join([
os.path.join(HOME_DIR, "output/tools/bin"),
os.environ.get("PATH", "")])
if IS_MACOS:
os.environ["ZERO_AR_DATE"] = "1"
def test(args):
"""test"""
LOG.info("begin test...")
if platform.system() == "Linux" or platform.system() == "Darwin":
set_cangjie_env(args)
unit_test(args)
LOG.info("end test...\n")
def unit_test(args):
"""unit test"""
LOG.info("begin unit test...\n")
output = subprocess.Popen(
["ctest", "--output-on-failure"], cwd=CMAKE_BUILD_DIR, stdout=PIPE
)
log_output(output)
LOG.info("end unit test...\n")
def install(args):
"""install targets"""
if args.host:
args.host = TARGET_DICTIONARY[args.host]
LOG.info("begin install targets...")
targets = []
if not args.host:
if os.path.exists(CMAKE_BUILD_DIR):
targets.append(("native", CMAKE_BUILD_DIR))
else:
suffix = "cjc-{}".format(args.host)
cross_build_path = os.path.join(BUILD_DIR, "build-{}".format(suffix))
if os.path.exists(cross_build_path):
targets.append((suffix, cross_build_path))
for directory in os.listdir(BUILD_DIR):
build_prefix = "build-libs-"
if directory.startswith(build_prefix):
targets.append(("libs-{}".format(directory[len(build_prefix):]), os.path.join(BUILD_DIR, directory)))
if len(targets) == 0:
LOG.fatal("Nothing is built yet.")
sys.exit(1)
for target in targets:
LOG.info("installing {} build...".format(target[0]))
cmake_cmd = ["cmake", "--install", "."]
if args.prefix:
cmake_cmd += ["--prefix", os.path.abspath(args.prefix)]
elif args.host:
cmake_cmd += ["--prefix", os.path.join(HOME_DIR, "output-{}".format(args.host))]
output = subprocess.Popen(cmake_cmd, cwd=target[1], stdout=PIPE)
log_output(output)
if output.returncode != 0:
LOG.fatal("install {} build failed".format(target[0]))
sys.exit(1)
if args.host == "x86_64-w64-mingw32":
mingw_bin_path = os.path.join(HOME_DIR, "output-x86_64-w64-mingw32/third_party/mingw/bin")
if os.path.exists(mingw_bin_path):
for bin in os.listdir(mingw_bin_path):
bin_path = os.path.join(mingw_bin_path, bin)
if os.path.isfile(bin_path) and not bin.endswith(".exe") and bin != "libssp-0.dll":
os.remove(bin_path)
LOG.info("end install targets...")
def redo_with_write(redo_func, path, err):
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWUSR)
redo_func(path)
else:
raise
def clean(args):
"""clean build outputs and logs"""
LOG.info("begin clean...\n")
handlerToBeRemoved = []
for handler in LOG.handlers:
if isinstance(handler, logging.FileHandler):
handler.close()
handlerToBeRemoved.append(handler)
for handler in handlerToBeRemoved:
LOG.removeHandler(handler)
output_dirs = []
output_dirs.append("build")
output_dirs.append("output")
output_dirs.append("third_party/binary/llvm_backend")
cross_output_suffix = []
for directory in os.listdir(BUILD_DIR):
build_prefix = "build-cjc-"
if directory.startswith(build_prefix):
cross_output_suffix.append(directory[len(build_prefix):])
for suffix in cross_output_suffix:
cross_output_path = os.path.join(HOME_DIR, "output-{}".format(suffix))
if os.path.isdir(cross_output_path):
output_dirs.append(cross_output_path)
for file_path in output_dirs:
abs_file_path = os.path.join(HOME_DIR, file_path)
if os.path.isdir(abs_file_path):
shutil.rmtree(abs_file_path, ignore_errors=False, onerror=redo_with_write)
if os.path.isfile(abs_file_path):
os.remove(abs_file_path)
LOG.info("end clean\n")
def package_mingw_dependencies(args):
if os.path.exists(os.path.join(HOME_DIR, "third_party/binary/windows-x86_64-mingw.tar.gz")):
return
LOG.info("Packaging mingw dependencies...")
def copy_files_to(from_path, filename_list, to_directory):
if not os.path.exists(to_directory):
os.makedirs(to_directory, exist_ok=True)
for filename in filename_list:
filepath = os.path.join(from_path, filename)
if os.path.exists(filepath):
shutil.copy(filepath, to_directory + "/")
else:
LOG.info("%s not found, skipped packaging the file...", filepath)
search_path = args.target_toolchain if args.target_toolchain else None
mingw_path = str(os.path.dirname(os.path.dirname(os.path.abspath(shutil.which("x86_64-w64-mingw32-gcc", path=search_path)))))
mingw_package_path = str(os.path.join(HOME_DIR, "third_party/binary/mingw"))
dll_dependencies = ["bin/libc++.dll", "bin/libunwind.dll", "bin/libwinpthread-1.dll"]
copy_files_to(os.path.join(mingw_path, "x86_64-w64-mingw32"), dll_dependencies, os.path.join(mingw_package_path, "dll"))
lib_dependencies = ["crt2.o", "dllcrt2.o", "libadvapi32.a", "libkernel32.a", "libm.a", "libmingw32.a",
"libmingwex.a", "libmoldname.a", "libmsvcrt.a", "libpthread.a", "libshell32.a", "libuser32.a",
"libc++.a", "libdbghelp.a", "libshlwapi.a", "libunwind.a",
"libws2_32.a", "libcrypt32.a", "crtbegin.o", "crtend.o", "CRT_glob.o"]
copy_files_to(os.path.join(mingw_path, "x86_64-w64-mingw32/lib"), lib_dependencies, os.path.join(mingw_package_path, "lib"))
subprocess.run(["tar", "-C", "mingw", "-zcf", "windows-x86_64-mingw.tar.gz", "dll", "lib"],
cwd=os.path.join(HOME_DIR, "third_party/binary"))
def init_log(name):
"""init log config"""
if not os.path.exists(LOG_DIR):
os.makedirs(LOG_DIR)
log = logging.getLogger(name)
log.setLevel(logging.DEBUG)
formatter = logging.Formatter(
"[%(asctime)s] [%(module)s] [%(levelname)s] %(message)s",
"%Y-%m-%d %H:%M:%S"
)
streamhandler = logging.StreamHandler(sys.stdout)
streamhandler.setLevel(logging.DEBUG)
streamhandler.setFormatter(formatter)
log.addHandler(streamhandler)
filehandler = TimedRotatingFileHandler(
LOG_FILE, when="W6", interval=1, backupCount=60
)
filehandler.setLevel(logging.DEBUG)
filehandler.setFormatter(formatter)
log.addHandler(filehandler)
return log
class BuildType(Enum):
"""CMAKE_BUILD_TYPE options"""
debug = "Debug"
release = "Release"
relwithdebinfo = "RelWithDebInfo"
def __str__(self):
return self.name
def __repr__(self):
return str(self)
@staticmethod
def argparse(s):
try:
return BuildType[s]
except KeyError:
return s.build_type
def main():
"""build entry"""
parser = argparse.ArgumentParser(description="build cangjie project")
subparsers = parser.add_subparsers(help="sub command help")
parser_clean = subparsers.add_parser("clean", help="clean build", add_help=False)
parser_clean.set_defaults(func=clean)
parser_build = subparsers.add_parser("build", help=" build cangjie")
parser_build.add_argument(
"-t", "--build-type", type=BuildType.argparse, dest="build_type", choices=list(BuildType), required=True,
help="select target build type"
)
parser_build.add_argument(
"--print-cmd", action="store_true", help="print the command to cmake without running"
)
parser_build.add_argument(
"-j", "--jobs", dest="jobs", type=int, default=0, help="run N jobs in parallel (0 means default)"
)
parser_build.add_argument(
"--link-jobs", dest="link_jobs", type=int, default=0, help="max N link jobs in parallel (0 means default)"
)
parser_build.add_argument(
"--enable-assert", action="store_true", help="build with assert and self-checking, only effective in release mode"
)
parser_build.add_argument(
"--no-tests", action="store_true", help="build without unittests"
)
parser_build.add_argument(
"--disable-stack-grow-feature", action="store_true", help="disable default stack grow feature for cjnative BE"
)
parser_build.add_argument(
"--hwasan", action="store_true",
help="build with hardware asan, only available when the target is ohos-aarch64 or ohos-x86_64"
)
parser_build.add_argument(
"--gcc-toolchain", dest="gcc_toolchain", help="Specify toolchain for cjc, stdlib & BE build"
)
parser_build.add_argument(
"--target", dest="target", type=str, choices=TARGET_DICTIONARY.keys(),
help="build a second stdlib for the target triple specified"
)
parser_build.add_argument(
"-L", "--target-lib", dest="target_lib", type=str, action='append', default=[],
help="link libraries under this path for all products"
)
parser_build.add_argument(
"--target-toolchain", dest="target_toolchain", type=str,
help="use the tools under the given path to cross-compile stdlib"
)
parser_build.add_argument(
"-I", "--include", dest="include", type=str, action='append', default=[],
help="search header files in given paths"
)
parser_build.add_argument(
"--target-sysroot", dest="target_sysroot", type=str,
help="pass this argument to C/CXX compiler as --sysroot"
)
parser_build.add_argument(
"--android-ndk", dest="android_ndk", type=str,
help="Specify the path to the android NDK"
)
product_types=['all', 'cjc', 'libs']
parser_build.add_argument(
"--product", dest="product", choices=product_types,
help="specify which part of Cangjie to build"
)
parser_build.add_argument(
"--build-cjdb", action="store_true",
help="build cjc with cjdb"
)
parser_build.add_argument(
"--cjdb-disable-python", action="store_true",
help="build cjdb without python extension"
)
parser_build.add_argument(
"--install-cjdb-script", action="store_true",
help="install cjdb with python script"
)
parser_build.add_argument(
"--use-oh-llvm-repo", action="store_true",
help="use OpenHarmony llvm repo with Cangjie llvm patch instead of cangjie llvm repo for building"
)
parser_build.add_argument(
"--cjlib-sanitizer-support",
type=str,
choices=["asan", "tsan", "hwasan"],
dest="sanitizer_support",
help="Enable cangjie sanitizer support for cangjie libraries."
)
parser_build.add_argument(
"--enable-sanitize-option", action="store_true",
help="enable --sanitize option for cjc"
)
parser_build.add_argument(
"-v", "--version",
dest="version",
default="0.0.1",
help="Version string, e.g., 0.0.1"
)
parser_build.set_defaults(func=build)
parser_install = subparsers.add_parser("install", help="install targets")
parser_install.add_argument(
"--host", dest="host", type=str, choices=TARGET_DICTIONARY.keys(),
help="Generate installation package for the host. When --prefix is specified, this option will not take effect"
)
parser_install.add_argument(
"--prefix", dest="prefix", help="target install prefix"
)
parser_install.set_defaults(func=install)
parser_test = subparsers.add_parser("test", help="run unittests", add_help=False)
parser_test.set_defaults(func=test)
args = parser.parse_args()
args.func(args)
def check_compiler(args):
toolchain_path = args.target_toolchain if args.target_toolchain else None
if toolchain_path and (not os.path.exists(toolchain_path)):
LOG.error("the given toolchain path does not exist, {}".format(toolchain_path))
if IS_WINDOWS and args.target is None:
c_compiler = shutil.which("x86_64-w64-mingw32-gcc.exe", path=toolchain_path)
cxx_compiler = shutil.which("x86_64-w64-mingw32-g++.exe", path=toolchain_path)
else:
c_compiler = shutil.which("clang", path=toolchain_path)
cxx_compiler = shutil.which("clang++", path=toolchain_path)
if (c_compiler == None or cxx_compiler == None) and args.target:
c_compiler = shutil.which(args.target + "-gcc", path=toolchain_path)
cxx_compiler = shutil.which(args.target + "-g++", path=toolchain_path)
if c_compiler == None or cxx_compiler == None:
c_compiler = shutil.which("gcc", path=toolchain_path)
cxx_compiler = shutil.which("g++", path=toolchain_path)
if c_compiler == None or cxx_compiler == None:
if toolchain_path:
LOG.error("cannot find available compiler in the given toolchain path")
else:
LOG.error("cannot find available compiler in $PATH")
LOG.error("clang/clang++ or gcc/g++ is required to build cangjie compiler")
if args.target and args.product == "cjc" and not c_compiler.endswith("clang"):
os.environ["CC"] = shutil.which("clang") + (" --gcc-toolchain={}".format(args.gcc_toolchain) if args.gcc_toolchain else "")
os.environ["CXX"] = shutil.which("clang++") + (" --gcc-toolchain={}".format(args.gcc_toolchain) if args.gcc_toolchain else "")
else:
os.environ["CC"] = c_compiler
os.environ["CXX"] = cxx_compiler
if __name__ == "__main__":
LOG = init_log("root")
os.environ["LANG"] = "C.UTF-8"
main()