| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Merges multiple OS-specific gyp dependency lists into one that works on all | 6 """Merges multiple OS-specific gyp dependency lists into one that works on all |
| 7 of them. | 7 of them. |
| 8 | 8 |
| 9 | 9 |
| 10 The logic is relatively simple. Takes the current conditions, add more | 10 The logic is relatively simple. Takes the current conditions, add more |
| 11 condition, find the strict subset. Done. | 11 condition, find the strict subset. Done. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 import copy | 14 import copy |
| 15 import logging | 15 import logging |
| 16 import optparse | 16 import optparse |
| 17 import re | 17 import re |
| 18 import sys | 18 import sys |
| 19 | 19 |
| 20 import trace_inputs | 20 import trace_inputs |
| 21 # Create shortcuts. |
| 22 from trace_inputs import KEY_TRACKED, KEY_UNTRACKED |
| 21 | 23 |
| 22 | 24 |
| 23 def union(lhs, rhs): | 25 def union(lhs, rhs): |
| 24 """Merges two compatible datastructures composed of dict/list/set.""" | 26 """Merges two compatible datastructures composed of dict/list/set.""" |
| 25 assert lhs is not None or rhs is not None | 27 assert lhs is not None or rhs is not None |
| 26 if lhs is None: | 28 if lhs is None: |
| 27 return copy.deepcopy(rhs) | 29 return copy.deepcopy(rhs) |
| 28 if rhs is None: | 30 if rhs is None: |
| 29 return copy.deepcopy(lhs) | 31 return copy.deepcopy(lhs) |
| 30 assert type(lhs) == type(rhs), (lhs, rhs) | 32 assert type(lhs) == type(rhs), (lhs, rhs) |
| 33 if hasattr(lhs, 'union'): |
| 34 # Includes set, OSSettings and Configs. |
| 35 return lhs.union(rhs) |
| 31 if isinstance(lhs, dict): | 36 if isinstance(lhs, dict): |
| 32 return dict((k, union(lhs.get(k), rhs.get(k))) for k in set(lhs).union(rhs)) | 37 return dict((k, union(lhs.get(k), rhs.get(k))) for k in set(lhs).union(rhs)) |
| 33 elif isinstance(lhs, set): | |
| 34 # Do not go inside the set. | |
| 35 return lhs.union(rhs) | |
| 36 elif isinstance(lhs, list): | 38 elif isinstance(lhs, list): |
| 37 # Do not go inside the list. | 39 # Do not go inside the list. |
| 38 return lhs + rhs | 40 return lhs + rhs |
| 39 assert False, type(lhs) | 41 assert False, type(lhs) |
| 40 | 42 |
| 41 | 43 |
| 42 def process_variables(for_os, variables): | |
| 43 """Extracts files and dirs from the |variables| dict. | |
| 44 | |
| 45 Returns a list of exactly two items. Each item is a dict that maps a string | |
| 46 to a set (of strings). | |
| 47 | |
| 48 In the first item, the keys are file names, and the values are sets of OS | |
| 49 names, like "win" or "mac". In the second item, the keys are directory names, | |
| 50 and the values are sets of OS names too. | |
| 51 """ | |
| 52 VALID_VARIABLES = ['isolate_files', 'isolate_dirs'] | |
| 53 | |
| 54 # Verify strictness. | |
| 55 assert isinstance(variables, dict), variables | |
| 56 assert set(VALID_VARIABLES).issuperset(set(variables)), variables.keys() | |
| 57 for items in variables.itervalues(): | |
| 58 assert isinstance(items, list), items | |
| 59 assert all(isinstance(i, basestring) for i in items), items | |
| 60 | |
| 61 # Returns [files, dirs] | |
| 62 return [ | |
| 63 dict((name, set([for_os])) for name in variables.get(var, [])) | |
| 64 for var in VALID_VARIABLES | |
| 65 ] | |
| 66 | |
| 67 | |
| 68 def eval_content(content): | 44 def eval_content(content): |
| 69 """Evaluates a GYP file and return the value defined in it.""" | 45 """Evaluates a GYP file and return the value defined in it.""" |
| 70 globs = {'__builtins__': None} | 46 globs = {'__builtins__': None} |
| 71 locs = {} | 47 locs = {} |
| 72 value = eval(content, globs, locs) | 48 value = eval(content, globs, locs) |
| 73 assert locs == {}, locs | 49 assert locs == {}, locs |
| 74 assert globs == {'__builtins__': None}, globs | 50 assert globs == {'__builtins__': None}, globs |
| 75 return value | 51 return value |
| 76 | 52 |
| 77 | 53 |
| 78 def _process_inner(for_os, inner, old_files, old_dirs, old_os): | 54 def verify_variables(variables): |
| 79 """Processes the variables inside a condition. | 55 """Verifies the |variables| dictionary is in the expected format.""" |
| 80 | 56 VALID_VARIABLES = [ |
| 81 Only meant to be called by parse_gyp_dict(). | 57 KEY_TRACKED, |
| 82 | 58 KEY_UNTRACKED, |
| 83 Args: | 59 'command', |
| 84 - for_os: OS where the references are tracked for. | 60 'read_only', |
| 85 - inner: Inner dictionary to process. | 61 ] |
| 86 - old_files: Previous list of files to union with. | 62 assert isinstance(variables, dict), variables |
| 87 - old_dirs: Previous list of directories to union with. | 63 assert set(VALID_VARIABLES).issuperset(set(variables)), variables.keys() |
| 88 - old_os: Previous list of OSes referenced to union with. | 64 for name, value in variables.iteritems(): |
| 89 | 65 if name == 'read_only': |
| 90 Returns: | 66 assert value in (True, False, None), value |
| 91 - A tuple of (files, dirs, os) where each list is a union of the new | 67 else: |
| 92 dependencies found for this OS, as referenced by for_os, and the previous | 68 assert isinstance(value, list), value |
| 93 list. | 69 assert all(isinstance(i, basestring) for i in value), value |
| 94 """ | 70 |
| 95 assert isinstance(inner, dict), inner | 71 |
| 96 assert set(['variables']).issuperset(set(inner)), inner.keys() | 72 def verify_condition(condition): |
| 97 new_files, new_dirs = process_variables(for_os, inner.get('variables', {})) | 73 """Verifies the |condition| dictionary is in the expected format.""" |
| 98 if new_files or new_dirs: | 74 VALID_INSIDE_CONDITION = ['variables'] |
| 99 old_os = old_os.union([for_os.lstrip('!')]) | 75 assert isinstance(condition, list), condition |
| 100 return union(old_files, new_files), union(old_dirs, new_dirs), old_os | 76 assert 2 <= len(condition) <= 3, condition |
| 101 | 77 assert re.match(r'OS==\"([a-z]+)\"', condition[0]), condition[0] |
| 102 | 78 for c in condition[1:]: |
| 103 def parse_gyp_dict(value): | 79 assert isinstance(c, dict), c |
| 104 """Parses a gyp dict as returned by eval_content(). | 80 assert set(VALID_INSIDE_CONDITION).issuperset(set(c)), c.keys() |
| 81 verify_variables(c.get('variables', {})) |
| 82 |
| 83 |
| 84 def verify_root(value): |
| 85 VALID_ROOTS = ['variables', 'conditions'] |
| 86 assert isinstance(value, dict), value |
| 87 assert set(VALID_ROOTS).issuperset(set(value)), value.keys() |
| 88 verify_variables(value.get('variables', {})) |
| 89 |
| 90 conditions = value.get('conditions', []) |
| 91 assert isinstance(conditions, list), conditions |
| 92 for condition in conditions: |
| 93 verify_condition(condition) |
| 94 |
| 95 |
| 96 class OSSettings(object): |
| 97 """Represents the dependencies for an OS. The structure is immutable.""" |
| 98 def __init__(self, name, values): |
| 99 self.name = name |
| 100 verify_variables(values) |
| 101 self.tracked = sorted(values.get(KEY_TRACKED, [])) |
| 102 self.untracked = sorted(values.get(KEY_UNTRACKED, [])) |
| 103 self.command = values.get('command', [])[:] |
| 104 self.read_only = values.get('read_only') |
| 105 |
| 106 def union(self, rhs): |
| 107 assert self.name == rhs.name |
| 108 assert not (self.command and rhs.command) |
| 109 var = { |
| 110 KEY_TRACKED: sorted(self.tracked + rhs.tracked), |
| 111 KEY_UNTRACKED: sorted(self.untracked + rhs.untracked), |
| 112 'command': self.command or rhs.command, |
| 113 'read_only': rhs.read_only if self.read_only is None else self.read_only, |
| 114 } |
| 115 return OSSettings(self.name, var) |
| 116 |
| 117 def flatten(self): |
| 118 out = {} |
| 119 if self.command: |
| 120 out['command'] = self.command |
| 121 if self.tracked: |
| 122 out[KEY_TRACKED] = self.tracked |
| 123 if self.untracked: |
| 124 out[KEY_UNTRACKED] = self.untracked |
| 125 if self.read_only is not None: |
| 126 out['read_only'] = self.read_only |
| 127 return out |
| 128 |
| 129 |
| 130 class Configs(object): |
| 131 """Represents all the OS-specific configurations. |
| 132 |
| 133 The self.per_os[None] member contains all the 'else' clauses plus the default |
| 134 values. It is not included in the flatten() result. |
| 135 """ |
| 136 def __init__(self, oses): |
| 137 self.per_os = { |
| 138 None: OSSettings(None, {}), |
| 139 } |
| 140 self.per_os.update(dict((name, OSSettings(name, {})) for name in oses)) |
| 141 |
| 142 def union(self, rhs): |
| 143 out = Configs(list(set(self.per_os.keys() + rhs.per_os.keys()))) |
| 144 for value in self.per_os.itervalues(): |
| 145 # TODO(maruel): FAIL |
| 146 out = out.union(value) |
| 147 for value in rhs.per_os.itervalues(): |
| 148 out = out.union(value) |
| 149 return out |
| 150 |
| 151 def add_globals(self, values): |
| 152 for key in self.per_os: |
| 153 self.per_os[key] = self.per_os[key].union(OSSettings(key, values)) |
| 154 |
| 155 def add_values(self, for_os, values): |
| 156 self.per_os[for_os] = self.per_os[for_os].union(OSSettings(for_os, values)) |
| 157 |
| 158 def add_negative_values(self, for_os, values): |
| 159 """Includes the variables to all OSes except |for_os|. |
| 160 |
| 161 This includes 'None' so unknown OSes gets it too. |
| 162 """ |
| 163 for key in self.per_os: |
| 164 if key != for_os: |
| 165 self.per_os[key] = self.per_os[key].union(OSSettings(key, values)) |
| 166 |
| 167 def flatten(self): |
| 168 """Returns a flat dictionary representation of the configuration. |
| 169 |
| 170 Skips None pseudo-OS. |
| 171 """ |
| 172 return dict( |
| 173 (k, v.flatten()) for k, v in self.per_os.iteritems() if k is not None) |
| 174 |
| 175 |
| 176 def invert_map(variables): |
| 177 """Converts a dict(OS, dict(deptype, list(dependencies)) to a flattened view. |
| 178 |
| 179 Returns a tuple of: |
| 180 1. dict(deptype, dict(dependency, set(OSes)) for easier processing. |
| 181 2. All the OSes found as a set. |
| 182 """ |
| 183 KEYS = ( |
| 184 KEY_TRACKED, |
| 185 KEY_UNTRACKED, |
| 186 'command', |
| 187 'read_only', |
| 188 ) |
| 189 out = dict((key, {}) for key in KEYS) |
| 190 for os_name, values in variables.iteritems(): |
| 191 for key in (KEY_TRACKED, KEY_UNTRACKED): |
| 192 for item in values.get(key, []): |
| 193 out[key].setdefault(item, set()).add(os_name) |
| 194 |
| 195 # command needs special handling. |
| 196 command = tuple(values.get('command', [])) |
| 197 out['command'].setdefault(command, set()).add(os_name) |
| 198 |
| 199 # read_only needs special handling. |
| 200 out['read_only'].setdefault(values.get('read_only'), set()).add(os_name) |
| 201 return out, set(variables) |
| 202 |
| 203 |
| 204 def reduce_inputs(values, oses): |
| 205 """Reduces the invert_map() output to the strictest minimum list. |
| 206 |
| 207 1. Construct the inverse map first. |
| 208 2. Look at each individual file and directory, map where they are used and |
| 209 reconstruct the inverse dictionary. |
| 210 3. Do not convert back to negative if only 2 OSes were merged. |
| 211 |
| 212 Returns a tuple of: |
| 213 1. the minimized dictionary |
| 214 2. oses passed through as-is. |
| 215 """ |
| 216 KEYS = ( |
| 217 KEY_TRACKED, |
| 218 KEY_UNTRACKED, |
| 219 'command', |
| 220 'read_only', |
| 221 ) |
| 222 out = dict((key, {}) for key in KEYS) |
| 223 assert all(oses), oses |
| 224 if len(oses) > 2: |
| 225 for key in KEYS: |
| 226 for item, item_oses in values.get(key, {}).iteritems(): |
| 227 # Converts all oses.difference('foo') to '!foo'. |
| 228 assert all(item_oses), item_oses |
| 229 missing = oses.difference(item_oses) |
| 230 if len(missing) == 1: |
| 231 # Replace it with a negative. |
| 232 out[key][item] = set(['!' + tuple(missing)[0]]) |
| 233 elif not missing: |
| 234 out[key][item] = set([None]) |
| 235 else: |
| 236 out[key][item] = set(item_oses) |
| 237 return out, oses |
| 238 |
| 239 |
| 240 def convert_map_to_gyp(values, oses): |
| 241 """Regenerates back a gyp-like configuration dict from files and dirs |
| 242 mappings generated from reduce_inputs(). |
| 243 """ |
| 244 # First, inverse the mapping to make it dict first. |
| 245 config = {} |
| 246 for key in values: |
| 247 for item, oses in values[key].iteritems(): |
| 248 if item is None: |
| 249 # For read_only default. |
| 250 continue |
| 251 for cond_os in oses: |
| 252 cond_key = None if cond_os is None else cond_os.lstrip('!') |
| 253 # Insert the if/else dicts. |
| 254 condition_values = config.setdefault(cond_key, [{}, {}]) |
| 255 # If condition is negative, use index 1, else use index 0. |
| 256 cond_value = condition_values[int((cond_os or '').startswith('!'))] |
| 257 variables = cond_value.setdefault('variables', {}) |
| 258 |
| 259 if item in (True, False): |
| 260 # One-off for read_only. |
| 261 variables[key] = item |
| 262 else: |
| 263 if isinstance(item, tuple): |
| 264 # One-off for command. |
| 265 # Do not merge lists and do not sort! |
| 266 # Note that item is a tuple. |
| 267 assert key not in variables |
| 268 variables[key] = list(item) |
| 269 else: |
| 270 # The list of items (files or dirs). Append the new item and keep |
| 271 # the list sorted. |
| 272 l = variables.setdefault(key, []) |
| 273 l.append(item) |
| 274 l.sort() |
| 275 |
| 276 out = {} |
| 277 for o in sorted(config): |
| 278 d = config[o] |
| 279 if o is None: |
| 280 assert not d[1] |
| 281 out = union(out, d[0]) |
| 282 else: |
| 283 c = out.setdefault('conditions', []) |
| 284 if d[1]: |
| 285 c.append(['OS=="%s"' % o] + d) |
| 286 else: |
| 287 c.append(['OS=="%s"' % o] + d[0:1]) |
| 288 return out |
| 289 |
| 290 |
| 291 def load_gyp(value): |
| 292 """Parses one gyp skeleton and returns a Configs() instance. |
| 105 | 293 |
| 106 |value| is the loaded dictionary that was defined in the gyp file. | 294 |value| is the loaded dictionary that was defined in the gyp file. |
| 107 | 295 |
| 108 Returns a 3-tuple, where the first two items are the same as the items | |
| 109 returned by process_variable() in the same order, and the last item is a set | |
| 110 of strings of all OSs seen in the input dict. | |
| 111 | |
| 112 The expected format is strict, anything diverting from the format below will | 296 The expected format is strict, anything diverting from the format below will |
| 113 fail: | 297 throw an assert: |
| 114 { | 298 { |
| 115 'variables': { | 299 'variables': { |
| 116 'isolate_files': [ | 300 'command': [ |
| 117 ... | 301 ... |
| 118 ], | 302 ], |
| 119 'isolate_dirs: [ | 303 'isolate_dependency_tracked': [ |
| 120 ... | 304 ... |
| 121 ], | 305 ], |
| 306 'isolate_dependency_untracked': [ |
| 307 ... |
| 308 ], |
| 309 'read_only': False, |
| 122 }, | 310 }, |
| 123 'conditions': [ | 311 'conditions': [ |
| 124 ['OS=="<os>"', { | 312 ['OS=="<os>"', { |
| 125 'variables': { | 313 'variables': { |
| 126 ... | 314 ... |
| 127 }, | 315 }, |
| 128 }, { # else | 316 }, { # else |
| 129 'variables': { | 317 'variables': { |
| 130 ... | 318 ... |
| 131 }, | 319 }, |
| 132 }], | 320 }], |
| 133 ... | 321 ... |
| 134 ], | 322 ], |
| 135 } | 323 } |
| 136 """ | 324 """ |
| 137 assert isinstance(value, dict), value | 325 verify_root(value) |
| 138 VALID_ROOTS = ['variables', 'conditions'] | 326 |
| 139 assert set(VALID_ROOTS).issuperset(set(value)), value.keys() | 327 # Scan to get the list of OSes. |
| 328 conditions = value.get('conditions', []) |
| 329 oses = set(re.match(r'OS==\"([a-z]+)\"', c[0]).group(1) for c in conditions) |
| 330 configs = Configs(oses) |
| 140 | 331 |
| 141 # Global level variables. | 332 # Global level variables. |
| 142 oses = set() | 333 configs.add_globals(value.get('variables', {})) |
| 143 files, dirs = process_variables(None, value.get('variables', {})) | |
| 144 | 334 |
| 145 # OS specific variables. | 335 # OS specific variables. |
| 146 conditions = value.get('conditions', []) | |
| 147 assert isinstance(conditions, list), conditions | |
| 148 for condition in conditions: | 336 for condition in conditions: |
| 149 assert isinstance(condition, list), condition | 337 condition_os = re.match(r'OS==\"([a-z]+)\"', condition[0]).group(1) |
| 150 assert 2 <= len(condition) <= 3, condition | 338 configs.add_values(condition_os, condition[1].get('variables', {})) |
| 151 m = re.match(r'OS==\"([a-z]+)\"', condition[0]) | 339 if len(condition) > 2: |
| 152 assert m, condition[0] | 340 configs.add_negative_values( |
| 153 condition_os = m.group(1) | 341 condition_os, condition[2].get('variables', {})) |
| 154 | 342 return configs |
| 155 files, dirs, oses = _process_inner( | |
| 156 condition_os, condition[1], files, dirs, oses) | |
| 157 | |
| 158 if len(condition) == 3: | |
| 159 files, dirs, oses = _process_inner( | |
| 160 '!' + condition_os, condition[2], files, dirs, oses) | |
| 161 | |
| 162 # TODO(maruel): _expand_negative() should be called here, because otherwise | |
| 163 # the OSes the negative condition represents is lost once the gyps are merged. | |
| 164 # This cause an invalid expansion in reduce_inputs() call. | |
| 165 return files, dirs, oses | |
| 166 | 343 |
| 167 | 344 |
| 168 def parse_gyp_dicts(gyps): | 345 def load_gyps(items): |
| 169 """Parses each gyp file and returns the merged results. | 346 """Parses each gyp file and returns the merged results. |
| 170 | 347 |
| 171 It only loads what parse_gyp_dict() can process. | 348 It only loads what load_gyp() can process. |
| 172 | 349 |
| 173 Return values: | 350 Return values: |
| 174 files: dict(filename, set(OS where this filename is a dependency)) | 351 files: dict(filename, set(OS where this filename is a dependency)) |
| 175 dirs: dict(dirame, set(OS where this dirname is a dependency)) | 352 dirs: dict(dirame, set(OS where this dirname is a dependency)) |
| 176 oses: set(all the OSes referenced) | 353 oses: set(all the OSes referenced) |
| 177 """ | 354 """ |
| 178 files = {} | 355 configs = Configs([]) |
| 179 dirs = {} | 356 for item in items: |
| 180 oses = set() | 357 configs = configs.union(load_gyp(eval_content(open(item, 'rb').read()))) |
| 181 for gyp in gyps: | 358 return configs |
| 182 with open(gyp, 'rb') as gyp_file: | |
| 183 content = gyp_file.read() | |
| 184 gyp_files, gyp_dirs, gyp_oses = parse_gyp_dict(eval_content(content)) | |
| 185 files = union(gyp_files, files) | |
| 186 dirs = union(gyp_dirs, dirs) | |
| 187 oses |= gyp_oses | |
| 188 return files, dirs, oses | |
| 189 | 359 |
| 190 | 360 |
| 191 def _expand_negative(items, oses): | 361 def main(args=None): |
| 192 """Converts all '!foo' value in the set by oses.difference('foo').""" | |
| 193 assert None not in oses and len(oses) >= 2, oses | |
| 194 for name in items: | |
| 195 if None in items[name]: | |
| 196 # Shortcut any item having None in their set. An item listed in None means | |
| 197 # the item is a dependency on all OSes. As such, there is no need to list | |
| 198 # any OS. | |
| 199 items[name] = set([None]) | |
| 200 continue | |
| 201 for neg in [o for o in items[name] if o.startswith('!')]: | |
| 202 # Replace it with the inverse. | |
| 203 items[name] = items[name].union(oses.difference([neg[1:]])) | |
| 204 items[name].remove(neg) | |
| 205 if items[name] == oses: | |
| 206 items[name] = set([None]) | |
| 207 | |
| 208 | |
| 209 def _compact_negative(items, oses): | |
| 210 """Converts all oses.difference('foo') to '!foo'. | |
| 211 | |
| 212 It is doing the reverse of _expand_negative(). | |
| 213 """ | |
| 214 assert None not in oses and len(oses) >= 3, oses | |
| 215 for name in items: | |
| 216 missing = oses.difference(items[name]) | |
| 217 if len(missing) == 1: | |
| 218 # Replace it with a negative. | |
| 219 items[name] = set(['!' + tuple(missing)[0]]) | |
| 220 | |
| 221 | |
| 222 def reduce_inputs(files, dirs, oses): | |
| 223 """Reduces the variables to their strictest minimum.""" | |
| 224 # Construct the inverse map first. | |
| 225 # Look at each individual file and directory, map where they are used and | |
| 226 # reconstruct the inverse dictionary. | |
| 227 # First, expands all '!' builders into the reverse. | |
| 228 # TODO(maruel): This is too late to call _expand_negative(). The exact list | |
| 229 # negative OSes condition it represents is lost at that point. | |
| 230 _expand_negative(files, oses) | |
| 231 _expand_negative(dirs, oses) | |
| 232 | |
| 233 # Do not convert back to negative if only 2 OSes were merged. It is easier to | |
| 234 # read this way. | |
| 235 if len(oses) > 2: | |
| 236 _compact_negative(files, oses) | |
| 237 _compact_negative(dirs, oses) | |
| 238 | |
| 239 return files, dirs | |
| 240 | |
| 241 | |
| 242 def convert_to_gyp(files, dirs): | |
| 243 """Regenerates back a gyp-like configuration dict from files and dirs | |
| 244 mappings. | |
| 245 | |
| 246 Sort the lists. | |
| 247 """ | |
| 248 # First, inverse the mapping to make it dict first. | |
| 249 config = {} | |
| 250 def to_cond(items, name): | |
| 251 for item, oses in items.iteritems(): | |
| 252 for cond_os in oses: | |
| 253 condition_values = config.setdefault( | |
| 254 None if cond_os is None else cond_os.lstrip('!'), | |
| 255 [{}, {}]) | |
| 256 # If condition is negative, use index 1, else use index 0. | |
| 257 condition_value = condition_values[int((cond_os or '').startswith('!'))] | |
| 258 # The list of items (files or dirs). Append the new item and keep the | |
| 259 # list sorted. | |
| 260 l = condition_value.setdefault('variables', {}).setdefault(name, []) | |
| 261 l.append(item) | |
| 262 l.sort() | |
| 263 | |
| 264 to_cond(files, 'isolate_files') | |
| 265 to_cond(dirs, 'isolate_dirs') | |
| 266 | |
| 267 out = {} | |
| 268 for o in sorted(config): | |
| 269 d = config[o] | |
| 270 if o is None: | |
| 271 assert not d[1] | |
| 272 out = union(out, d[0]) | |
| 273 else: | |
| 274 c = out.setdefault('conditions', []) | |
| 275 if d[1]: | |
| 276 c.append(['OS=="%s"' % o] + d) | |
| 277 else: | |
| 278 c.append(['OS=="%s"' % o] + d[0:1]) | |
| 279 return out | |
| 280 | |
| 281 | |
| 282 def main(): | |
| 283 parser = optparse.OptionParser( | 362 parser = optparse.OptionParser( |
| 284 usage='%prog <options> [file1] [file2] ...') | 363 usage='%prog <options> [file1] [file2] ...') |
| 285 parser.add_option( | 364 parser.add_option( |
| 286 '-v', '--verbose', action='count', default=0, help='Use multiple times') | 365 '-v', '--verbose', action='count', default=0, help='Use multiple times') |
| 287 | 366 |
| 288 options, args = parser.parse_args() | 367 options, args = parser.parse_args(args) |
| 289 level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] | 368 level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] |
| 290 logging.basicConfig( | 369 logging.basicConfig( |
| 291 level=level, | 370 level=level, |
| 292 format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s') | 371 format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s') |
| 293 | 372 |
| 294 trace_inputs.pretty_print( | 373 trace_inputs.pretty_print( |
| 295 convert_to_gyp(*reduce_inputs(*parse_gyp_dicts(args))), | 374 convert_map_to_gyp( |
| 375 *reduce_inputs( |
| 376 *invert_map( |
| 377 load_gyps(args).flatten()))), |
| 296 sys.stdout) | 378 sys.stdout) |
| 297 return 0 | 379 return 0 |
| 298 | 380 |
| 299 | 381 |
| 300 if __name__ == '__main__': | 382 if __name__ == '__main__': |
| 301 sys.exit(main()) | 383 sys.exit(main()) |
| OLD | NEW |