Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: nodejs binding #16

Merged
merged 8 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .gnfiles/build/scripts/get_tsconfig_outdir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#
# Copyright © 2025 Agora
# This file is part of TEN Framework, an open source project.
# Licensed under the Apache License, Version 2.0, with certain conditions.
# Refer to the "LICENSE" file in the root directory for more information.
#
import json
import sys
import re


def read_ts_config(file_name):
with open(file_name) as f:
ts_config = f.read()

# Remove comments and redundant commas before JSON parsing, because they
# are not valid JSON contents.

str_pattern = r'"(?:\\.|[^"])*"'

# Remove comments.
def keep_str_remove_comments(m):
if m.group(0).startswith("/"):
return ""
else:
return m.group(0)

ts_config = re.sub(
str_pattern + r"|/\*[.\n]+?\*/|//[^\n]*",
keep_str_remove_comments,
ts_config,
)

# Remove redundant commas.
def keep_str_remove_redundant_commas(m):
if m.group(0).startswith(","):
return m.group(0)[1:]
else:
return m.group(0)

ts_config = re.sub(
str_pattern + r"|,(?:[\s\n]*[\]\}])",
keep_str_remove_redundant_commas,
ts_config,
)

# Parse ts_config.json.
config = json.loads(ts_config)
return config


if __name__ == "__main__":
# Outputs depends on tsconfig.json, but not including this file.
ts_config = read_ts_config(sys.argv[1])
print(ts_config["compilerOptions"]["outDir"])
133 changes: 133 additions & 0 deletions .gnfiles/build/scripts/glob_tsconfig_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#
# Copyright © 2025 Agora
# This file is part of TEN Framework, an open source project.
# Licensed under the Apache License, Version 2.0, with certain conditions.
# Refer to the "LICENSE" file in the root directory for more information.
#
import json
import sys
import re
import glob
import os
import fnmatch


# Supports file matching with recursive wildcards `**`, while excluding the
# `node_modules/` directory.
def proper_glob(src_pattern: str) -> list[str]:
if "**" in src_pattern:
srcs = []
folder, pattern = src_pattern.split("**")
if not folder:
folder = "."
pattern = pattern.strip("/")
for root, dirnames, filenames in os.walk(folder):
dirnames[:] = [d for d in dirnames if d != "node_modules"]
for filename in fnmatch.filter(filenames, pattern):
srcs.append(os.path.join(root, filename))
return srcs
else:
return glob.glob(src_pattern)


def read_ts_config(file_name):
with open(file_name) as f:
ts_config = f.read()

# Remove comments and redundant commas before JSON parsing, because they
# are not valid JSON contents.

str_pattern = r'"(?:\\.|[^"])*"'

# Remove comments.
def keep_str_remove_comments(m):
if m.group(0).startswith("/"):
return ""
else:
return m.group(0)

ts_config = re.sub(
str_pattern + r"|/\*[.\n]+?\*/|//[^\n]*",
keep_str_remove_comments,
ts_config,
)

# Remove redundant commas.
def keep_str_remove_redundant_commas(m):
if m.group(0).startswith(","):
return m.group(0)[1:]
else:
return m.group(0)

ts_config = re.sub(
str_pattern + r"|,(?:[\s\n]*[\]\}])",
keep_str_remove_redundant_commas,
ts_config,
)

# Parse ts_config.json.
config = json.loads(ts_config)
return config


# Lists the source files or output files that match the criteria specified in
# the `include` and `exclude` fields of the `tsconfig.json` file.
def glob_ts_sources(ts_config_file, ts_config, out_dir=None):
# Remember current working directory so that we can come back here.
dir = os.path.abspath(".")

try:
source_folder = os.path.dirname(os.path.abspath(ts_config_file))
os.chdir(source_folder)

srcs = []
if "include" in ts_config:
for src_pattern in ts_config["include"]:
srcs += proper_glob(src_pattern)
else:
srcs += proper_glob("**/*")

if "exclude" in ts_config:
for src_pattern in ts_config["exclude"]:
new_srcs = []
exclude_srcs = proper_glob(src_pattern)
for src in srcs:
if src not in exclude_srcs:
new_srcs.append(src)
srcs = new_srcs

results = []
for line in srcs:
if out_dir:
# Replaces the `.ts` extension in the source file paths with
# `.js` and places them in the specified output directory. This
# assumes all source files are `.ts` files and the output files
# are `.js` files.
results.append(
out_dir.rstrip("/")
+ "/"
+ re.sub(r"\.ts$", ".js", line, re.IGNORECASE)
)
else:
results.append(os.path.join(source_folder, line))

return results
finally:
# Go back to the original working directory.
os.chdir(dir)


if __name__ == "__main__":
if len(sys.argv) > 2:
out_dir = sys.argv[2]
else:
out_dir = None

# outputs depends on tsconfig.json, but not including this file
if not out_dir:
print(sys.argv[1])

ts_config = read_ts_config(sys.argv[1])
sources = glob_ts_sources(sys.argv[1], ts_config, out_dir)
for source in sources:
print(source)
154 changes: 154 additions & 0 deletions .gnfiles/build/scripts/npm_install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#
# Copyright © 2025 Agora
# This file is part of TEN Framework, an open source project.
# Licensed under the Apache License, Version 2.0, with certain conditions.
# Refer to the "LICENSE" file in the root directory for more information.
#
import os
import sys
import subprocess
import argparse
from build.scripts import fs_utils, log, cmd_exec


class NpmInstall:
def __init__(self, args) -> None:
self.args = args
self.show_extra_log(
"npm_install.py\n"
f" project_dir: {self.args.project_dir}\n"
f" tsconfig: {self.args.package_json}\n"
f" output_dir: {self.args.output_dir}\n"
f" platform: {self.args.platform}\n"
)

def show_extra_log(self, str: str) -> None:
if self.args.log_level >= 1:
log.info(str)

def is_tsc_exist(self) -> bool:
cmd = "tsc --version"
status, _ = cmd_exec.run_cmd_realtime(
cmd, log_level=self.args.log_level
)
if status == 0:
return True

if sys.platform == "win32":
tsc = os.path.join("node_modules", ".bin", "tsc.cmd")
else:
tsc = os.path.join("node_modules", ".bin", "tsc")

if os.path.exists(tsc):
return True
else:
return False

def check_npm_version(self) -> None:
if sys.platform == "win32":
my_cmd = ["cmd", "/c", "npm", "--version"]
else:
my_cmd = ["npm", "--version"]
child = subprocess.Popen(
my_cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
encoding="utf-8",
)
stdout, stderr = child.communicate()
if child.returncode:
log.error(f"Stderr:\n{stderr}")
exit(-1)
if int(stdout.split(".")[0]) < 8:
log.error("Your npm version less than 8, please upgrade")
exit(-1)

def install(self):
os.chdir(args.output_dir)
for i in range(3):
try:
if os.path.exists("package-lock.json"):
if not os.path.exists("node_modules") or os.path.getmtime(
"package-lock.json"
) > os.path.getmtime("node_modules"):
# npm ci: fail if lock file not satisfied.
self.show_extra_log(
"npm install (npm ci), because package-lock.json"
" exists."
)

cmd = "npm ci"
if self.args.log_level == 1:
cmd += " --loglevel verbose"
elif self.args.log_level == 2:
cmd += " --loglevel silly"

cmd_exec.run_cmd_realtime(
cmd, log_level=self.args.log_level
)
else:
self.show_extra_log(
"npm skip install (npm i), because"
" package-lock.json mtime:"
f" {os.path.getmtime('package-lock.json')} <"
" node_modules mtime"
f" {os.path.getmtime('node_modules')}"
)

# If tsc not found, we should update package-lock.json
# mtime and call npm ci again.
if not self.is_tsc_exist():
os.utime("package-lock.json")
else:
self.show_extra_log(
"npm install (npm i), because package-lock.json not"
" exists."
)

cmd = "npm install"
if self.args.log_level == 1:
cmd += " --loglevel verbose"
elif self.args.log_level == 2:
cmd += " --loglevel silly"

cmd_exec.run_cmd_realtime(
cmd, log_level=self.args.log_level
)
except Exception as exc:
self.show_extra_log(f"Failed to npm install: {exc}")
else:
self.show_extra_log("npm install success.")
break
if not self.is_tsc_exist():
raise Exception(
"tsc not found in current npm package and global node dir,"
" please check."
)

def run(self):
fs_utils.copy(
self.args.package_json,
os.path.join(self.args.output_dir, "package.json"),
)
if self.args.package_lock_json:
fs_utils.copy(
self.args.package_lock_json,
os.path.join(self.args.output_dir, "package-lock.json"),
)
self.check_npm_version()
self.install()


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--project-dir", type=str, required=True)
parser.add_argument("--package-json", type=str, required=True)
parser.add_argument("--package-lock-json", type=str, required=False)
parser.add_argument("--output-dir", type=str, required=True)
parser.add_argument("--platform", type=str, required=True)
parser.add_argument("--log-level", type=int, default=0, required=True)
args = parser.parse_args()

ni = NpmInstall(args)
ni.run()
Loading
Loading