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