makefile.py (9625B)
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. 6 from __future__ import print_function 7 8 from . import * 9 from .. import * 10 from .. import utils 11 from ..request_types import * 12 13 import platform 14 15 def get_gnumake_rules(build_dirs, requests, makefile_vars, **kwargs): 16 makefile_string = "" 17 18 # Common Variables 19 common_vars = kwargs["common_vars"] 20 for key, value in sorted(makefile_vars.items()): 21 makefile_string += "{KEY} = {VALUE}\n".format( 22 KEY = key, 23 VALUE = value 24 ) 25 makefile_string += "\n" 26 27 # Directories 28 dirs_timestamp_file = "{TMP_DIR}/dirs.timestamp".format(**common_vars) 29 makefile_string += "DIRS = {TIMESTAMP_FILE}\n\n".format( 30 TIMESTAMP_FILE = dirs_timestamp_file 31 ) 32 makefile_string += "{TIMESTAMP_FILE}:\n\t$(MKINSTALLDIRS) {ALL_DIRS}\n\techo timestamp > {TIMESTAMP_FILE}\n\n".format( 33 TIMESTAMP_FILE = dirs_timestamp_file, 34 ALL_DIRS = " ".join(build_dirs).format(**common_vars) 35 ) 36 37 # Generate Rules 38 make_rules = [] 39 for request in requests: 40 make_rules += get_gnumake_rules_helper(request, **kwargs) 41 42 # Main Commands 43 for rule in make_rules: 44 if isinstance(rule, MakeFilesVar): 45 makefile_string += "{NAME} = {FILE_LIST}\n\n".format( 46 NAME = rule.name, 47 FILE_LIST = files_to_makefile(rule.files, wrap = True, **kwargs), 48 ) 49 continue 50 51 if isinstance(rule, MakeStringVar): 52 makefile_string += "define {NAME}\n{CONTENT}\nendef\nexport {NAME}\n\n".format( 53 NAME = rule.name, 54 CONTENT = rule.content 55 ) 56 continue 57 58 assert isinstance(rule, MakeRule) 59 header_line = "{OUT_FILE}: {DEP_FILES} {DEP_LITERALS} | $(DIRS)".format( 60 OUT_FILE = files_to_makefile([rule.output_file], **kwargs), 61 DEP_FILES = files_to_makefile(rule.dep_files, wrap = True, **kwargs), 62 DEP_LITERALS = " ".join(rule.dep_literals) 63 ) 64 65 if len(rule.cmds) == 0: 66 makefile_string += "%s\n\n" % header_line 67 continue 68 69 makefile_string += "{HEADER_LINE}\n{RULE_LINES}\n\n".format( 70 HEADER_LINE = header_line, 71 RULE_LINES = "\n".join("\t%s" % cmd for cmd in rule.cmds) 72 ) 73 74 return makefile_string 75 76 def files_to_makefile(files, common_vars, wrap = False, **kwargs): 77 if len(files) == 0: 78 return "" 79 dirnames = [utils.dir_for(file).format(**common_vars) for file in files] 80 join_str = " \\\n\t\t" if wrap and len(files) > 2 else " " 81 if len(files) == 1: 82 return "%s/%s" % (dirnames[0], files[0].filename) 83 elif len(set(dirnames)) == 1: 84 return "$(addprefix %s/,%s)" % (dirnames[0], join_str.join(file.filename for file in files)) 85 else: 86 return join_str.join("%s/%s" % (d, f.filename) for d,f in zip(dirnames, files)) 87 88 def get_gnumake_rules_helper(request, common_vars, **kwargs): 89 90 if isinstance(request, PrintFileRequest): 91 var_name = "%s_CONTENT" % request.name.upper() 92 platform_os = platform.system() 93 cmds = [] 94 if platform_os in ["OS390", "OS/390", "zos"] and request.shall_be_utf8: 95 cmds.append( 96 '''echo "$${VAR_NAME}" | iconv -f IBM-1047 -T -t UTF-8 > {MAKEFILENAME}'''.format( 97 VAR_NAME = var_name, 98 MAKEFILENAME = files_to_makefile([request.output_file], common_vars), 99 **common_vars 100 )) 101 cmds.append( 102 '''sed -i '1s/^/\\xef\\xbb\\xbf/' {MAKEFILENAME}'''.format( 103 MAKEFILENAME = files_to_makefile([request.output_file], common_vars), 104 **common_vars 105 )) 106 else: 107 cmds.append("echo \"$${VAR_NAME}\" > {MAKEFILENAME}".format( 108 VAR_NAME = var_name, 109 MAKEFILENAME = files_to_makefile([request.output_file], common_vars), 110 **common_vars 111 )) 112 return [ 113 MakeStringVar( 114 name = var_name, 115 content = request.content 116 ), 117 MakeRule( 118 name = request.name, 119 dep_literals = [], 120 dep_files = [], 121 output_file = request.output_file, 122 cmds = cmds 123 ) 124 ] 125 126 127 if isinstance(request, CopyRequest): 128 return [ 129 MakeRule( 130 name = request.name, 131 dep_literals = [], 132 dep_files = [request.input_file], 133 output_file = request.output_file, 134 cmds = ["cp %s %s" % ( 135 files_to_makefile([request.input_file], common_vars), 136 files_to_makefile([request.output_file], common_vars)) 137 ] 138 ) 139 ] 140 141 if isinstance(request, VariableRequest): 142 return [ 143 MakeFilesVar( 144 name = request.name.upper(), 145 files = request.input_files 146 ) 147 ] 148 149 if request.tool.name == "make": 150 cmd_template = "$(MAKE) {ARGS}" 151 elif request.tool.name == "gentest": 152 cmd_template = "$(INVOKE) $(GENTEST) {ARGS}" 153 else: 154 assert isinstance(request.tool, IcuTool) 155 cmd_template = "$(INVOKE) $(TOOLBINDIR)/{TOOL} {{ARGS}}".format( 156 TOOL = request.tool.name 157 ) 158 159 if isinstance(request, SingleExecutionRequest): 160 cmd = utils.format_single_request_command(request, cmd_template, common_vars) 161 dep_files = request.all_input_files() 162 163 if len(request.output_files) > 1: 164 # Special case for multiple output files: Makefile rules should have only one 165 # output file apiece. More information: 166 # https://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html 167 timestamp_var_name = "%s_ALL" % request.name.upper() 168 timestamp_file = TmpFile("%s.timestamp" % request.name) 169 rules = [ 170 MakeFilesVar( 171 name = timestamp_var_name, 172 files = [timestamp_file] 173 ), 174 MakeRule( 175 name = "%s_all" % request.name, 176 dep_literals = [], 177 dep_files = dep_files, 178 output_file = timestamp_file, 179 cmds = [ 180 cmd, 181 "echo timestamp > {MAKEFILENAME}".format( 182 MAKEFILENAME = files_to_makefile([timestamp_file], common_vars) 183 ) 184 ] 185 ) 186 ] 187 for i, file in enumerate(request.output_files): 188 rules += [ 189 MakeRule( 190 name = "%s_%d" % (request.name, i), 191 dep_literals = ["$(%s)" % timestamp_var_name], 192 dep_files = [], 193 output_file = file, 194 cmds = [] 195 ) 196 ] 197 return rules 198 199 elif len(dep_files) > 5: 200 # For nicer printing, for long input lists, use a helper variable. 201 dep_var_name = "%s_DEPS" % request.name.upper() 202 return [ 203 MakeFilesVar( 204 name = dep_var_name, 205 files = dep_files 206 ), 207 MakeRule( 208 name = request.name, 209 dep_literals = ["$(%s)" % dep_var_name], 210 dep_files = [], 211 output_file = request.output_files[0], 212 cmds = [cmd] 213 ) 214 ] 215 216 else: 217 return [ 218 MakeRule( 219 name = request.name, 220 dep_literals = [], 221 dep_files = dep_files, 222 output_file = request.output_files[0], 223 cmds = [cmd] 224 ) 225 ] 226 227 if isinstance(request, RepeatedExecutionRequest): 228 rules = [] 229 dep_literals = [] 230 # To keep from repeating the same dep files many times, make a variable. 231 if len(request.common_dep_files) > 0: 232 dep_var_name = "%s_DEPS" % request.name.upper() 233 dep_literals = ["$(%s)" % dep_var_name] 234 rules += [ 235 MakeFilesVar( 236 name = dep_var_name, 237 files = request.common_dep_files 238 ) 239 ] 240 # Add a rule for each individual file. 241 for loop_vars in utils.repeated_execution_request_looper(request): 242 (_, specific_dep_files, input_file, output_file) = loop_vars 243 name_suffix = input_file[input_file.filename.rfind("/")+1:input_file.filename.rfind(".")] 244 cmd = utils.format_repeated_request_command( 245 request, 246 cmd_template, 247 loop_vars, 248 common_vars 249 ) 250 rules += [ 251 MakeRule( 252 name = "%s_%s" % (request.name, name_suffix), 253 dep_literals = dep_literals, 254 dep_files = specific_dep_files + [input_file], 255 output_file = output_file, 256 cmds = [cmd] 257 ) 258 ] 259 return rules 260 261 assert False