1# Copyright (C) 2018 and later: Unicode, Inc. and others.
2# License & terms of use: http://www.unicode.org/copyright.html
3
4from . import *
5from .. import *
6from .. import utils
7from ..request_types import *
8
9import os
10import shutil
11import subprocess
12import sys
13
14def run(build_dirs, requests, common_vars, verbose=True, **kwargs):
15    for bd in build_dirs:
16        makedirs(bd.format(**common_vars))
17    for request in requests:
18        status = run_helper(request, common_vars, verbose=verbose, **kwargs)
19        if status != 0:
20            print("!!! ERROR executing above command line: exit code %d" % status)
21            return 1
22    if verbose:
23        print("All data build commands executed")
24    return 0
25
26def makedirs(dirs):
27    """makedirs compatible between Python 2 and 3"""
28    try:
29        # Python 3 version
30        os.makedirs(dirs, exist_ok=True)
31    except TypeError as e:
32        # Python 2 version
33        try:
34            os.makedirs(dirs)
35        except OSError as e:
36            if e.errno != errno.EEXIST:
37                raise e
38
39def run_helper(request, common_vars, platform, tool_dir, verbose, tool_cfg=None, **kwargs):
40    if isinstance(request, PrintFileRequest):
41        output_path = "{DIRNAME}/{FILENAME}".format(
42            DIRNAME = utils.dir_for(request.output_file).format(**common_vars),
43            FILENAME = request.output_file.filename,
44        )
45        if verbose:
46            print("Printing to file: %s" % output_path)
47        with open(output_path, "w") as f:
48            f.write(request.content)
49        return 0
50    if isinstance(request, CopyRequest):
51        input_path = "{DIRNAME}/{FILENAME}".format(
52            DIRNAME = utils.dir_for(request.input_file).format(**common_vars),
53            FILENAME = request.input_file.filename,
54        )
55        output_path = "{DIRNAME}/{FILENAME}".format(
56            DIRNAME = utils.dir_for(request.output_file).format(**common_vars),
57            FILENAME = request.output_file.filename,
58        )
59        if verbose:
60            print("Copying file to: %s" % output_path)
61        shutil.copyfile(input_path, output_path)
62        return 0
63    if isinstance(request, VariableRequest):
64        # No-op
65        return 0
66
67    assert isinstance(request.tool, IcuTool)
68    if platform == "windows":
69        cmd_template = "{TOOL_DIR}/{TOOL}/{TOOL_CFG}/{TOOL}.exe {{ARGS}}".format(
70            TOOL_DIR = tool_dir,
71            TOOL_CFG = tool_cfg,
72            TOOL = request.tool.name,
73            **common_vars
74        )
75    elif platform == "unix":
76        cmd_template = "{TOOL_DIR}/{TOOL} {{ARGS}}".format(
77            TOOL_DIR = tool_dir,
78            TOOL = request.tool.name,
79            **common_vars
80        )
81    elif platform == "bazel":
82        cmd_template = "{TOOL_DIR}/{TOOL}/{TOOL} {{ARGS}}".format(
83            TOOL_DIR = tool_dir,
84            TOOL = request.tool.name,
85            **common_vars
86        )
87    else:
88        raise ValueError("Unknown platform: %s" % platform)
89
90    if isinstance(request, RepeatedExecutionRequest):
91        for loop_vars in utils.repeated_execution_request_looper(request):
92            command_line = utils.format_repeated_request_command(
93                request,
94                cmd_template,
95                loop_vars,
96                common_vars
97            )
98            if platform == "windows":
99                # Note: this / to \ substitution may be too aggressive?
100                command_line = command_line.replace("/", "\\")
101            returncode = run_shell_command(command_line, platform, verbose)
102            if returncode != 0:
103                return returncode
104        return 0
105    if isinstance(request, SingleExecutionRequest):
106        command_line = utils.format_single_request_command(
107            request,
108            cmd_template,
109            common_vars
110        )
111        if platform == "windows":
112            # Note: this / to \ substitution may be too aggressive?
113            command_line = command_line.replace("/", "\\")
114        returncode = run_shell_command(command_line, platform, verbose)
115        return returncode
116    assert False
117
118def run_shell_command(command_line, platform, verbose):
119    changed_windows_comspec = False
120    # If the command line length on Windows exceeds the absolute maximum that CMD supports (8191), then
121    # we temporarily switch over to use PowerShell for the command, and then switch back to CMD.
122    # We don't want to use PowerShell for everything though, as it tends to be slower.
123    if (platform == "windows"):
124        previous_comspec = os.environ["COMSPEC"]
125        # Add 7 to the length for the argument /c with quotes.
126        # For example:  C:\WINDOWS\system32\cmd.exe /c "<command_line>"
127        if ((len(previous_comspec) + len(command_line) + 7) > 8190):
128            if verbose:
129                print("Command length exceeds the max length for CMD on Windows, using PowerShell instead.")
130            os.environ["COMSPEC"] = 'powershell'
131            changed_windows_comspec = True
132    if verbose:
133        print("Running: %s" % command_line)
134        returncode = subprocess.call(
135            command_line,
136            shell = True
137        )
138    else:
139        # Pipe output to /dev/null in quiet mode
140        with open(os.devnull, "w") as devnull:
141            returncode = subprocess.call(
142                command_line,
143                shell = True,
144                stdout = devnull,
145                stderr = devnull
146            )
147    if changed_windows_comspec:
148        os.environ["COMSPEC"] = previous_comspec
149    return returncode
150