summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/python/icutools/databuilder/renderers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /intl/icu/source/python/icutools/databuilder/renderers
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/icu/source/python/icutools/databuilder/renderers')
-rw-r--r--intl/icu/source/python/icutools/databuilder/renderers/__init__.py10
-rw-r--r--intl/icu/source/python/icutools/databuilder/renderers/common_exec.py155
-rw-r--r--intl/icu/source/python/icutools/databuilder/renderers/makefile.py245
3 files changed, 410 insertions, 0 deletions
diff --git a/intl/icu/source/python/icutools/databuilder/renderers/__init__.py b/intl/icu/source/python/icutools/databuilder/renderers/__init__.py
new file mode 100644
index 0000000000..7c402c2b78
--- /dev/null
+++ b/intl/icu/source/python/icutools/databuilder/renderers/__init__.py
@@ -0,0 +1,10 @@
+# Copyright (C) 2018 and later: Unicode, Inc. and others.
+# License & terms of use: http://www.unicode.org/copyright.html
+
+from collections import namedtuple
+
+MakeRule = namedtuple("MakeRule", ["name", "dep_literals", "dep_files", "output_file", "cmds"])
+
+MakeFilesVar = namedtuple("MakeFilesVar", ["name", "files"])
+
+MakeStringVar = namedtuple("MakeStringVar", ["name", "content"])
diff --git a/intl/icu/source/python/icutools/databuilder/renderers/common_exec.py b/intl/icu/source/python/icutools/databuilder/renderers/common_exec.py
new file mode 100644
index 0000000000..91c12fdcf6
--- /dev/null
+++ b/intl/icu/source/python/icutools/databuilder/renderers/common_exec.py
@@ -0,0 +1,155 @@
+# Copyright (C) 2018 and later: Unicode, Inc. and others.
+# License & terms of use: http://www.unicode.org/copyright.html
+
+# Python 2/3 Compatibility (ICU-20299)
+# TODO(ICU-20301): Remove this.
+from __future__ import print_function
+
+from . import *
+from .. import *
+from .. import utils
+from ..request_types import *
+
+import os
+import shutil
+import subprocess
+import sys
+
+def run(build_dirs, requests, common_vars, verbose=True, **kwargs):
+ for bd in build_dirs:
+ makedirs(bd.format(**common_vars))
+ for request in requests:
+ status = run_helper(request, common_vars, verbose=verbose, **kwargs)
+ if status != 0:
+ print("!!! ERROR executing above command line: exit code %d" % status)
+ return 1
+ if verbose:
+ print("All data build commands executed")
+ return 0
+
+def makedirs(dirs):
+ """makedirs compatible between Python 2 and 3"""
+ try:
+ # Python 3 version
+ os.makedirs(dirs, exist_ok=True)
+ except TypeError as e:
+ # Python 2 version
+ try:
+ os.makedirs(dirs)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise e
+
+def run_helper(request, common_vars, platform, tool_dir, verbose, tool_cfg=None, **kwargs):
+ if isinstance(request, PrintFileRequest):
+ output_path = "{DIRNAME}/{FILENAME}".format(
+ DIRNAME = utils.dir_for(request.output_file).format(**common_vars),
+ FILENAME = request.output_file.filename,
+ )
+ if verbose:
+ print("Printing to file: %s" % output_path)
+ with open(output_path, "w") as f:
+ f.write(request.content)
+ return 0
+ if isinstance(request, CopyRequest):
+ input_path = "{DIRNAME}/{FILENAME}".format(
+ DIRNAME = utils.dir_for(request.input_file).format(**common_vars),
+ FILENAME = request.input_file.filename,
+ )
+ output_path = "{DIRNAME}/{FILENAME}".format(
+ DIRNAME = utils.dir_for(request.output_file).format(**common_vars),
+ FILENAME = request.output_file.filename,
+ )
+ if verbose:
+ print("Copying file to: %s" % output_path)
+ shutil.copyfile(input_path, output_path)
+ return 0
+ if isinstance(request, VariableRequest):
+ # No-op
+ return 0
+
+ assert isinstance(request.tool, IcuTool)
+ if platform == "windows":
+ cmd_template = "{TOOL_DIR}/{TOOL}/{TOOL_CFG}/{TOOL}.exe {{ARGS}}".format(
+ TOOL_DIR = tool_dir,
+ TOOL_CFG = tool_cfg,
+ TOOL = request.tool.name,
+ **common_vars
+ )
+ elif platform == "unix":
+ cmd_template = "{TOOL_DIR}/{TOOL} {{ARGS}}".format(
+ TOOL_DIR = tool_dir,
+ TOOL = request.tool.name,
+ **common_vars
+ )
+ elif platform == "bazel":
+ cmd_template = "{TOOL_DIR}/{TOOL}/{TOOL} {{ARGS}}".format(
+ TOOL_DIR = tool_dir,
+ TOOL = request.tool.name,
+ **common_vars
+ )
+ else:
+ raise ValueError("Unknown platform: %s" % platform)
+
+ if isinstance(request, RepeatedExecutionRequest):
+ for loop_vars in utils.repeated_execution_request_looper(request):
+ command_line = utils.format_repeated_request_command(
+ request,
+ cmd_template,
+ loop_vars,
+ common_vars
+ )
+ if platform == "windows":
+ # Note: this / to \ substitution may be too aggressive?
+ command_line = command_line.replace("/", "\\")
+ returncode = run_shell_command(command_line, platform, verbose)
+ if returncode != 0:
+ return returncode
+ return 0
+ if isinstance(request, SingleExecutionRequest):
+ command_line = utils.format_single_request_command(
+ request,
+ cmd_template,
+ common_vars
+ )
+ if platform == "windows":
+ # Note: this / to \ substitution may be too aggressive?
+ command_line = command_line.replace("/", "\\")
+ returncode = run_shell_command(command_line, platform, verbose)
+ return returncode
+ assert False
+
+def run_shell_command(command_line, platform, verbose):
+ changed_windows_comspec = False
+ # If the command line length on Windows exceeds the absolute maximum that CMD supports (8191), then
+ # we temporarily switch over to use PowerShell for the command, and then switch back to CMD.
+ # We don't want to use PowerShell for everything though, as it tends to be slower.
+ if (platform == "windows"):
+ previous_comspec = os.environ["COMSPEC"]
+ # Add 7 to the length for the argument /c with quotes.
+ # For example: C:\WINDOWS\system32\cmd.exe /c "<command_line>"
+ if ((len(previous_comspec) + len(command_line) + 7) > 8190):
+ if verbose:
+ print("Command length exceeds the max length for CMD on Windows, using PowerShell instead.")
+ os.environ["COMSPEC"] = 'powershell'
+ changed_windows_comspec = True
+ if verbose:
+ print("Running: %s" % command_line)
+ returncode = subprocess.call(
+ command_line,
+ shell = True
+ )
+ else:
+ # Pipe output to /dev/null in quiet mode
+ with open(os.devnull, "w") as devnull:
+ returncode = subprocess.call(
+ command_line,
+ shell = True,
+ stdout = devnull,
+ stderr = devnull
+ )
+ if changed_windows_comspec:
+ os.environ["COMSPEC"] = previous_comspec
+ if returncode != 0:
+ print("Command failed: %s" % command_line, file=sys.stderr)
+ return returncode
diff --git a/intl/icu/source/python/icutools/databuilder/renderers/makefile.py b/intl/icu/source/python/icutools/databuilder/renderers/makefile.py
new file mode 100644
index 0000000000..9b2005b07d
--- /dev/null
+++ b/intl/icu/source/python/icutools/databuilder/renderers/makefile.py
@@ -0,0 +1,245 @@
+# Copyright (C) 2018 and later: Unicode, Inc. and others.
+# License & terms of use: http://www.unicode.org/copyright.html
+
+# Python 2/3 Compatibility (ICU-20299)
+# TODO(ICU-20301): Remove this.
+from __future__ import print_function
+
+from . import *
+from .. import *
+from .. import utils
+from ..request_types import *
+
+def get_gnumake_rules(build_dirs, requests, makefile_vars, **kwargs):
+ makefile_string = ""
+
+ # Common Variables
+ common_vars = kwargs["common_vars"]
+ for key, value in sorted(makefile_vars.items()):
+ makefile_string += "{KEY} = {VALUE}\n".format(
+ KEY = key,
+ VALUE = value
+ )
+ makefile_string += "\n"
+
+ # Directories
+ dirs_timestamp_file = "{TMP_DIR}/dirs.timestamp".format(**common_vars)
+ makefile_string += "DIRS = {TIMESTAMP_FILE}\n\n".format(
+ TIMESTAMP_FILE = dirs_timestamp_file
+ )
+ makefile_string += "{TIMESTAMP_FILE}:\n\t$(MKINSTALLDIRS) {ALL_DIRS}\n\techo timestamp > {TIMESTAMP_FILE}\n\n".format(
+ TIMESTAMP_FILE = dirs_timestamp_file,
+ ALL_DIRS = " ".join(build_dirs).format(**common_vars)
+ )
+
+ # Generate Rules
+ make_rules = []
+ for request in requests:
+ make_rules += get_gnumake_rules_helper(request, **kwargs)
+
+ # Main Commands
+ for rule in make_rules:
+ if isinstance(rule, MakeFilesVar):
+ makefile_string += "{NAME} = {FILE_LIST}\n\n".format(
+ NAME = rule.name,
+ FILE_LIST = files_to_makefile(rule.files, wrap = True, **kwargs),
+ )
+ continue
+
+ if isinstance(rule, MakeStringVar):
+ makefile_string += "define {NAME}\n{CONTENT}\nendef\nexport {NAME}\n\n".format(
+ NAME = rule.name,
+ CONTENT = rule.content
+ )
+ continue
+
+ assert isinstance(rule, MakeRule)
+ header_line = "{OUT_FILE}: {DEP_FILES} {DEP_LITERALS} | $(DIRS)".format(
+ OUT_FILE = files_to_makefile([rule.output_file], **kwargs),
+ DEP_FILES = files_to_makefile(rule.dep_files, wrap = True, **kwargs),
+ DEP_LITERALS = " ".join(rule.dep_literals)
+ )
+
+ if len(rule.cmds) == 0:
+ makefile_string += "%s\n\n" % header_line
+ continue
+
+ makefile_string += "{HEADER_LINE}\n{RULE_LINES}\n\n".format(
+ HEADER_LINE = header_line,
+ RULE_LINES = "\n".join("\t%s" % cmd for cmd in rule.cmds)
+ )
+
+ return makefile_string
+
+def files_to_makefile(files, common_vars, wrap = False, **kwargs):
+ if len(files) == 0:
+ return ""
+ dirnames = [utils.dir_for(file).format(**common_vars) for file in files]
+ join_str = " \\\n\t\t" if wrap and len(files) > 2 else " "
+ if len(files) == 1:
+ return "%s/%s" % (dirnames[0], files[0].filename)
+ elif len(set(dirnames)) == 1:
+ return "$(addprefix %s/,%s)" % (dirnames[0], join_str.join(file.filename for file in files))
+ else:
+ return join_str.join("%s/%s" % (d, f.filename) for d,f in zip(dirnames, files))
+
+def get_gnumake_rules_helper(request, common_vars, **kwargs):
+
+ if isinstance(request, PrintFileRequest):
+ var_name = "%s_CONTENT" % request.name.upper()
+ return [
+ MakeStringVar(
+ name = var_name,
+ content = request.content
+ ),
+ MakeRule(
+ name = request.name,
+ dep_literals = [],
+ dep_files = [],
+ output_file = request.output_file,
+ cmds = [
+ "echo \"$${VAR_NAME}\" > {MAKEFILENAME}".format(
+ VAR_NAME = var_name,
+ MAKEFILENAME = files_to_makefile([request.output_file], common_vars),
+ **common_vars
+ )
+ ]
+ )
+ ]
+
+
+ if isinstance(request, CopyRequest):
+ return [
+ MakeRule(
+ name = request.name,
+ dep_literals = [],
+ dep_files = [request.input_file],
+ output_file = request.output_file,
+ cmds = ["cp %s %s" % (
+ files_to_makefile([request.input_file], common_vars),
+ files_to_makefile([request.output_file], common_vars))
+ ]
+ )
+ ]
+
+ if isinstance(request, VariableRequest):
+ return [
+ MakeFilesVar(
+ name = request.name.upper(),
+ files = request.input_files
+ )
+ ]
+
+ if request.tool.name == "make":
+ cmd_template = "$(MAKE) {ARGS}"
+ elif request.tool.name == "gentest":
+ cmd_template = "$(INVOKE) $(GENTEST) {ARGS}"
+ else:
+ assert isinstance(request.tool, IcuTool)
+ cmd_template = "$(INVOKE) $(TOOLBINDIR)/{TOOL} {{ARGS}}".format(
+ TOOL = request.tool.name
+ )
+
+ if isinstance(request, SingleExecutionRequest):
+ cmd = utils.format_single_request_command(request, cmd_template, common_vars)
+ dep_files = request.all_input_files()
+
+ if len(request.output_files) > 1:
+ # Special case for multiple output files: Makefile rules should have only one
+ # output file apiece. More information:
+ # https://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html
+ timestamp_var_name = "%s_ALL" % request.name.upper()
+ timestamp_file = TmpFile("%s.timestamp" % request.name)
+ rules = [
+ MakeFilesVar(
+ name = timestamp_var_name,
+ files = [timestamp_file]
+ ),
+ MakeRule(
+ name = "%s_all" % request.name,
+ dep_literals = [],
+ dep_files = dep_files,
+ output_file = timestamp_file,
+ cmds = [
+ cmd,
+ "echo timestamp > {MAKEFILENAME}".format(
+ MAKEFILENAME = files_to_makefile([timestamp_file], common_vars)
+ )
+ ]
+ )
+ ]
+ for i, file in enumerate(request.output_files):
+ rules += [
+ MakeRule(
+ name = "%s_%d" % (request.name, i),
+ dep_literals = ["$(%s)" % timestamp_var_name],
+ dep_files = [],
+ output_file = file,
+ cmds = []
+ )
+ ]
+ return rules
+
+ elif len(dep_files) > 5:
+ # For nicer printing, for long input lists, use a helper variable.
+ dep_var_name = "%s_DEPS" % request.name.upper()
+ return [
+ MakeFilesVar(
+ name = dep_var_name,
+ files = dep_files
+ ),
+ MakeRule(
+ name = request.name,
+ dep_literals = ["$(%s)" % dep_var_name],
+ dep_files = [],
+ output_file = request.output_files[0],
+ cmds = [cmd]
+ )
+ ]
+
+ else:
+ return [
+ MakeRule(
+ name = request.name,
+ dep_literals = [],
+ dep_files = dep_files,
+ output_file = request.output_files[0],
+ cmds = [cmd]
+ )
+ ]
+
+ if isinstance(request, RepeatedExecutionRequest):
+ rules = []
+ dep_literals = []
+ # To keep from repeating the same dep files many times, make a variable.
+ if len(request.common_dep_files) > 0:
+ dep_var_name = "%s_DEPS" % request.name.upper()
+ dep_literals = ["$(%s)" % dep_var_name]
+ rules += [
+ MakeFilesVar(
+ name = dep_var_name,
+ files = request.common_dep_files
+ )
+ ]
+ # Add a rule for each individual file.
+ for loop_vars in utils.repeated_execution_request_looper(request):
+ (_, specific_dep_files, input_file, output_file) = loop_vars
+ name_suffix = input_file[input_file.filename.rfind("/")+1:input_file.filename.rfind(".")]
+ cmd = utils.format_repeated_request_command(
+ request,
+ cmd_template,
+ loop_vars,
+ common_vars
+ )
+ rules += [
+ MakeRule(
+ name = "%s_%s" % (request.name, name_suffix),
+ dep_literals = dep_literals,
+ dep_files = specific_dep_files + [input_file],
+ output_file = output_file,
+ cmds = [cmd]
+ )
+ ]
+ return rules
+
+ assert False