1 # Copyright (C) 2018 and later: Unicode, Inc. and others.
2 # License & terms of use: http://www.unicode.org/copyright.html
4 # Python 2/3 Compatibility (ICU-20299)
5 # TODO(ICU-20301): Remove this.
6 from __future__
import print_function
8 from abc
import abstractmethod
16 # TODO(ICU-20301): Remove arguments from all instances of super() in this file
18 # Note: for this to be a proper abstract class, it should extend abc.ABC.
19 # There is no nice way to do this that works in both Python 2 and 3.
20 # TODO(ICU-20301): Make this inherit from abc.ABC.
21 class AbstractRequest(object):
22 def __init__(self
, **kwargs
):
24 # Used for identification purposes
27 # The filter category that applies to this request
30 self
._set
_fields
(kwargs
)
32 def _set_fields(self
, kwargs
):
33 for key
, value
in list(kwargs
.items()):
34 if hasattr(self
, key
):
35 if isinstance(value
, list):
36 value
= copy
.copy(value
)
37 elif isinstance(value
, dict):
38 value
= copy
.deepcopy(value
)
39 setattr(self
, key
, value
)
41 raise ValueError("Unknown argument: %s" % key
)
43 def apply_file_filter(self
, filter):
45 Returns True if this request still has input files after filtering,
46 or False if the request is "empty" after filtering.
50 def flatten(self
, config
, all_requests
, common_vars
):
53 def all_input_files(self
):
56 def all_output_files(self
):
60 class AbstractExecutionRequest(AbstractRequest
):
61 def __init__(self
, **kwargs
):
63 # Names of targets (requests) or files that this request depends on.
64 # The entries of dep_targets may be any of the following types:
66 # 1. DepTarget, for the output of an execution request.
67 # 2. InFile, TmpFile, etc., for a specific file.
68 # 3. A list of InFile, TmpFile, etc., where the list is the same
69 # length as self.input_files and self.output_files.
71 # In cases 1 and 2, the dependency is added to all rules that the
72 # request generates. In case 3, the dependency is added only to the
73 # rule that generates the output file at the same array index.
76 # Computed during self.flatten(); don't edit directly.
77 self
.common_dep_files
= []
82 # Output files; for some subclasses, this must be the same length
84 self
.output_files
= []
86 # What tool to execute
89 # Argument string to pass to the tool with optional placeholders
92 # Placeholders to substitute into the argument string; if any of these
93 # have a list type, the list must be equal in length to input_files
96 super(AbstractExecutionRequest
, self
).__init
__(**kwargs
)
98 def apply_file_filter(self
, filter):
100 while i
< len(self
.input_files
):
101 if filter.match(self
.input_files
[i
]):
107 def _del_at(self
, i
):
108 del self
.input_files
[i
]
109 for _
, v
in self
.format_with
.items():
110 if isinstance(v
, list):
111 assert len(v
) == len(self
.input_files
) + 1
113 for v
in self
.dep_targets
:
114 if isinstance(v
, list):
115 assert len(v
) == len(self
.input_files
) + 1
118 def flatten(self
, config
, all_requests
, common_vars
):
119 self
._dep
_targets
_to
_files
(all_requests
)
120 return super(AbstractExecutionRequest
, self
).flatten(config
, all_requests
, common_vars
)
122 def _dep_targets_to_files(self
, all_requests
):
123 if not self
.dep_targets
:
125 for dep_target
in self
.dep_targets
:
126 if isinstance(dep_target
, list):
127 if hasattr(self
, "specific_dep_files"):
128 assert len(dep_target
) == len(self
.specific_dep_files
)
129 for file, out_list
in zip(dep_target
, self
.specific_dep_files
):
130 assert hasattr(file, "filename")
131 out_list
.append(file)
133 self
.common_dep_files
+= dep_target
135 if not isinstance(dep_target
, DepTarget
):
136 # Copy file entries directly to dep_files.
137 assert hasattr(dep_target
, "filename")
138 self
.common_dep_files
.append(dep_target
)
140 # For DepTarget entries, search for the target.
141 for request
in all_requests
:
142 if request
.name
== dep_target
.name
:
143 self
.common_dep_files
+= request
.all_output_files()
146 print("Warning: Unable to find target %s, a dependency of %s" % (
151 def all_input_files(self
):
152 return self
.common_dep_files
+ self
.input_files
154 def all_output_files(self
):
155 return self
.output_files
158 class SingleExecutionRequest(AbstractExecutionRequest
):
159 def __init__(self
, **kwargs
):
160 super(SingleExecutionRequest
, self
).__init
__(**kwargs
)
163 class RepeatedExecutionRequest(AbstractExecutionRequest
):
164 def __init__(self
, **kwargs
):
166 # Placeholders to substitute into the argument string unique to each
167 # iteration; all values must be lists equal in length to input_files
168 self
.repeat_with
= {}
170 # Lists for dep files that are specific to individual resource bundle files
171 self
.specific_dep_files
= [[] for _
in range(len(kwargs
["input_files"]))]
173 super(RepeatedExecutionRequest
, self
).__init
__(**kwargs
)
175 def _del_at(self
, i
):
176 super(RepeatedExecutionRequest
, self
)._del
_at
(i
)
177 del self
.output_files
[i
]
178 for _
, v
in self
.repeat_with
.items():
179 if isinstance(v
, list):
182 def all_input_files(self
):
183 files
= super(RepeatedExecutionRequest
, self
).all_input_files()
184 for specific_file_list
in self
.specific_dep_files
:
185 files
+= specific_file_list
189 class RepeatedOrSingleExecutionRequest(AbstractExecutionRequest
):
190 def __init__(self
, **kwargs
):
191 self
.repeat_with
= {}
192 super(RepeatedOrSingleExecutionRequest
, self
).__init
__(**kwargs
)
194 def flatten(self
, config
, all_requests
, common_vars
):
195 if config
.max_parallel
:
196 new_request
= RepeatedExecutionRequest(
198 category
= self
.category
,
199 dep_targets
= self
.dep_targets
,
200 input_files
= self
.input_files
,
201 output_files
= self
.output_files
,
204 format_with
= self
.format_with
,
205 repeat_with
= self
.repeat_with
208 new_request
= SingleExecutionRequest(
210 category
= self
.category
,
211 dep_targets
= self
.dep_targets
,
212 input_files
= self
.input_files
,
213 output_files
= self
.output_files
,
216 format_with
= utils
.concat_dicts(self
.format_with
, self
.repeat_with
)
218 return new_request
.flatten(config
, all_requests
, common_vars
)
220 def _del_at(self
, i
):
221 super(RepeatedOrSingleExecutionRequest
, self
)._del
_at
(i
)
222 del self
.output_files
[i
]
223 for _
, v
in self
.repeat_with
.items():
224 if isinstance(v
, list):
228 class PrintFileRequest(AbstractRequest
):
229 def __init__(self
, **kwargs
):
230 self
.output_file
= None
232 super(PrintFileRequest
, self
).__init
__(**kwargs
)
234 def all_output_files(self
):
235 return [self
.output_file
]
238 class CopyRequest(AbstractRequest
):
239 def __init__(self
, **kwargs
):
240 self
.input_file
= None
241 self
.output_file
= None
242 super(CopyRequest
, self
).__init
__(**kwargs
)
244 def all_input_files(self
):
245 return [self
.input_file
]
247 def all_output_files(self
):
248 return [self
.output_file
]
251 class VariableRequest(AbstractRequest
):
252 def __init__(self
, **kwargs
):
253 self
.input_files
= []
254 super(VariableRequest
, self
).__init
__(**kwargs
)
256 def all_input_files(self
):
257 return self
.input_files
260 class ListRequest(AbstractRequest
):
261 def __init__(self
, **kwargs
):
262 self
.variable_name
= None
263 self
.output_file
= None
264 self
.include_tmp
= None
265 super(ListRequest
, self
).__init
__(**kwargs
)
267 def flatten(self
, config
, all_requests
, common_vars
):
268 list_files
= list(sorted(utils
.get_all_output_files(all_requests
)))
270 variable_files
= list(sorted(utils
.get_all_output_files(all_requests
, include_tmp
=True)))
272 # Always include the list file itself
273 variable_files
= list_files
+ [self
.output_file
]
274 return PrintFileRequest(
276 output_file
= self
.output_file
,
277 content
= "\n".join(file.filename
for file in list_files
)
278 ).flatten(config
, all_requests
, common_vars
) + VariableRequest(
279 name
= self
.variable_name
,
280 input_files
= variable_files
281 ).flatten(config
, all_requests
, common_vars
)
283 def all_output_files(self
):
284 return [self
.output_file
]
287 class IndexTxtRequest(AbstractRequest
):
288 def __init__(self
, **kwargs
):
289 self
.input_files
= []
290 self
.output_file
= None
291 self
.cldr_version
= ""
292 super(IndexTxtRequest
, self
).__init
__(**kwargs
)
294 def apply_file_filter(self
, filter):
296 while i
< len(self
.input_files
):
297 if filter.match(self
.input_files
[i
]):
300 del self
.input_files
[i
]
303 def flatten(self
, config
, all_requests
, common_vars
):
304 return PrintFileRequest(
306 output_file
= self
.output_file
,
307 content
= self
._generate
_index
_file
(common_vars
)
308 ).flatten(config
, all_requests
, common_vars
)
310 def _generate_index_file(self
, common_vars
):
311 locales
= [f
.filename
[f
.filename
.rfind("/")+1:-4] for f
in self
.input_files
]
312 formatted_version
= " CLDRVersion { \"%s\" }\n" % self
.cldr_version
if self
.cldr_version
else ""
313 formatted_locales
= "\n".join([" %s {\"\"}" % v
for v
in locales
])
314 # TODO: CLDRVersion is required only in the base file
315 return ("// Warning this file is automatically generated\n"
316 "{INDEX_NAME}:table(nofallback) {{\n"
317 "{FORMATTED_VERSION}"
318 " InstalledLocales {{\n"
319 "{FORMATTED_LOCALES}\n"
322 FORMATTED_VERSION
= formatted_version
,
323 FORMATTED_LOCALES
= formatted_locales
,
327 def all_input_files(self
):
328 return self
.input_files
330 def all_output_files(self
):
331 return [self
.output_file
]