import os
import stat
import subprocess
import sys
import argparse
import json
import fnmatch
from pathlib import Path
INTEROP_ETS_LIST = ["api", "arkts", "kits"]
OUTPUT_PATH = ''
CONFIG_JSON = "interface/sdk-js/compile_ets_ts.json"
INTEROP_NAME = "static-interop"
def should_exclude(path, config):
dirname = os.path.basename(os.path.dirname(path))
if any(fnmatch.fnmatch(dirname, pattern) for pattern in config['excluded_dirs']):
return True
filename = os.path.basename(path)
if any(fnmatch.fnmatch(filename, pattern) for pattern in config['excluded_files']):
return True
ext = os.path.splitext(path)[1].lower()
if ext in config['excluded_extensions']:
return True
return False
def walk_with_exclusions(root_dir, file_folder_dir, config_path=CONFIG_JSON):
config = {}
with open(os.path.join(root_dir, config_path), 'r') as f:
config = json.load(f)
for root, dirs, files in os.walk(file_folder_dir):
dirs[:] = [d for d in dirs if not should_exclude(
os.path.join(root, d), config)]
filtered_files = [f for f in files if not should_exclude(
os.path.join(root, f), config)]
yield root, dirs, filtered_files
def build_ets_tool_config(root_build_dir, tool_dir, output_dir, out_interop_path, subdir):
global OUTPUT_PATH
target_dir = os.path.join(tool_dir, subdir)
OUTPUT_PATH = os.path.abspath(os.path.join(
out_interop_path, INTEROP_NAME, f"dependence-json/ets_tool_config_json_{subdir}.json"))
module_root_path = str(target_dir)
all_files = []
for dirpath, dirnames, filenames in walk_with_exclusions(root_build_dir, target_dir):
files = [os.path.join(dirpath, file) for file in filenames]
all_files.extend(files)
config = {
"compileFiles": all_files,
"packageName": "",
"buildType": "build",
"buildMode": "Release",
"moduleRootPath": module_root_path,
"sourceRoots": ["./"],
"loaderOutPath": str(os.path.abspath(os.path.join(out_interop_path, INTEROP_NAME))),
"cachePath": str(os.path.abspath(os.path.join(out_interop_path, INTEROP_NAME, "cache"))),
"buildSdkPath": str(tool_dir),
"dependentModuleList": [],
"plugins": [],
"isIDE": "false",
"maxWorkers": 16,
"skipDeclCheck": True,
"enableDeclgenEts2Ts": True,
"declgenV1OutPath": str(os.path.abspath(os.path.join(out_interop_path, INTEROP_NAME, "declaration", subdir))),
"declgenBridgeCodePath": str(os.path.abspath(os.path.join(out_interop_path, INTEROP_NAME, "bridge", subdir))),
}
try:
out_path_dir = Path(OUTPUT_PATH).resolve()
Path(out_path_dir).parent.mkdir(parents=True, exist_ok=True)
flags = os.O_WRONLY | os.O_CREAT
mode = stat.S_IWUSR | stat.S_IRUSR
with os.fdopen(os.open(out_path_dir, flags, mode), 'w', encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
return str(out_path_dir)
except Exception as e:
print(f"run_compile_ets_ts: {str(e)}")
raise Exception(f"Error generate declgen config json failed")
def run_compile_ets_ts(tool_dir: str, node_path: str, config_json_path: str, out_interop_path: str, subdir):
panda_path = os.path.join(tool_dir, "build-tools/ets2panda/lib")
tool_path = os.path.join(
tool_dir, "build-tools/driver/build-system/dist/entry.js")
node_path = os.path.abspath(node_path)
env = os.environ.copy()
env["LD_LIBRARY_PATH"] = str(panda_path)
try:
interop_path_declaration = os.path.join(out_interop_path, "static-interop/declaration", subdir)
interop_path_bridge = os.path.join(out_interop_path, "static-interop/bridge", subdir)
os.makedirs(interop_path_declaration, exist_ok=True)
os.makedirs(interop_path_bridge, exist_ok=True)
cmd = [node_path, tool_path, config_json_path]
result = subprocess.run(cmd, env=env, check=True,
cwd=tool_dir, text=True, capture_output=True)
with open(os.path.abspath(os.path.join(out_interop_path, INTEROP_NAME, f"interop_tool_subdir_{subdir}.log")), 'w', encoding='utf-8') as f:
f.write(f"=== Output from interop1.2 tool ({subdir}) ===\n")
f.write(result.stdout)
check_interop_path_exists(interop_path_declaration)
check_interop_path_exists(interop_path_bridge)
print(f"run_compile_ets_ts ({subdir}) success: {result.returncode}")
except subprocess.CalledProcessError as e:
print(f"run_compile_ets_ts ({subdir}) error: {e.returncode}")
print("run_compile_ets_ts:", e.stderr)
check_interop_path_exists(interop_path_declaration)
check_interop_path_exists(interop_path_bridge)
def check_interop_path_exists(input_path: str):
if not os.path.exists(input_path):
raise FileNotFoundError(f"Missing interop directory: {input_path}")
def move_static_record_to_root(out_interop_path: str, subdir: str):
"""Move static.Record.d.ts from subdir to root declaration directory"""
src_path = os.path.join(out_interop_path, "static-interop/declaration", subdir, "static.Record.d.ts")
dst_path = os.path.join(out_interop_path, "static-interop/declaration/static.Record.d.ts")
if os.path.exists(src_path):
if os.path.exists(dst_path):
os.remove(src_path)
print(f"Removed duplicate static.Record.d.ts from {subdir}")
else:
os.rename(src_path, dst_path)
print(f"Moved static.Record.d.ts from {subdir} to root")
def run_compile_ets_ts_main():
parser = argparse.ArgumentParser()
parser.add_argument('--root-build-dir', required=True)
parser.add_argument('--tool-dir', required=True)
parser.add_argument('--output-interface-sdk', required=True)
parser.add_argument('--output-interop-sdk', required=True)
parser.add_argument('--node-path', required=True)
options = parser.parse_args()
options.tool_dir = os.path.abspath(options.tool_dir)
out_interop_path = os.path.abspath(options.output_interop_sdk)
output_interface_sdk = os.path.abspath(options.output_interface_sdk)
for subdir in INTEROP_ETS_LIST:
config_json = build_ets_tool_config(
options.root_build_dir, options.tool_dir,
output_interface_sdk, out_interop_path, subdir)
run_compile_ets_ts(options.tool_dir, options.node_path,
config_json, out_interop_path, subdir)
move_static_record_to_root(out_interop_path, subdir)
if __name__ == "__main__":
sys.exit(run_compile_ets_ts_main())