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