Index: tools/isolate/merge_gyp.py |
diff --git a/tools/isolate/merge_gyp.py b/tools/isolate/merge_gyp.py |
deleted file mode 100755 |
index fd4a0d3b67a992c9889dfb7a945df5a2e2b30ef7..0000000000000000000000000000000000000000 |
--- a/tools/isolate/merge_gyp.py |
+++ /dev/null |
@@ -1,301 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Merges multiple OS-specific gyp dependency lists into one that works on all |
-of them. |
- |
- |
-The logic is relatively simple. Takes the current conditions, add more |
-condition, find the strict subset. Done. |
-""" |
- |
-import copy |
-import logging |
-import optparse |
-import re |
-import sys |
- |
-import trace_inputs |
- |
- |
-def union(lhs, rhs): |
- """Merges two compatible datastructures composed of dict/list/set.""" |
- assert lhs is not None or rhs is not None |
- if lhs is None: |
- return copy.deepcopy(rhs) |
- if rhs is None: |
- return copy.deepcopy(lhs) |
- assert type(lhs) == type(rhs), (lhs, rhs) |
- if isinstance(lhs, dict): |
- return dict((k, union(lhs.get(k), rhs.get(k))) for k in set(lhs).union(rhs)) |
- elif isinstance(lhs, set): |
- # Do not go inside the set. |
- return lhs.union(rhs) |
- elif isinstance(lhs, list): |
- # Do not go inside the list. |
- return lhs + rhs |
- assert False, type(lhs) |
- |
- |
-def process_variables(for_os, variables): |
- """Extracts files and dirs from the |variables| dict. |
- |
- Returns a list of exactly two items. Each item is a dict that maps a string |
- to a set (of strings). |
- |
- In the first item, the keys are file names, and the values are sets of OS |
- names, like "win" or "mac". In the second item, the keys are directory names, |
- and the values are sets of OS names too. |
- """ |
- VALID_VARIABLES = ['isolate_files', 'isolate_dirs'] |
- |
- # Verify strictness. |
- assert isinstance(variables, dict), variables |
- assert set(VALID_VARIABLES).issuperset(set(variables)), variables.keys() |
- for items in variables.itervalues(): |
- assert isinstance(items, list), items |
- assert all(isinstance(i, basestring) for i in items), items |
- |
- # Returns [files, dirs] |
- return [ |
- dict((name, set([for_os])) for name in variables.get(var, [])) |
- for var in VALID_VARIABLES |
- ] |
- |
- |
-def eval_content(content): |
- """Evaluates a GYP file and return the value defined in it.""" |
- globs = {'__builtins__': None} |
- locs = {} |
- value = eval(content, globs, locs) |
- assert locs == {}, locs |
- assert globs == {'__builtins__': None}, globs |
- return value |
- |
- |
-def _process_inner(for_os, inner, old_files, old_dirs, old_os): |
- """Processes the variables inside a condition. |
- |
- Only meant to be called by parse_gyp_dict(). |
- |
- Args: |
- - for_os: OS where the references are tracked for. |
- - inner: Inner dictionary to process. |
- - old_files: Previous list of files to union with. |
- - old_dirs: Previous list of directories to union with. |
- - old_os: Previous list of OSes referenced to union with. |
- |
- Returns: |
- - A tuple of (files, dirs, os) where each list is a union of the new |
- dependencies found for this OS, as referenced by for_os, and the previous |
- list. |
- """ |
- assert isinstance(inner, dict), inner |
- assert set(['variables']).issuperset(set(inner)), inner.keys() |
- new_files, new_dirs = process_variables(for_os, inner.get('variables', {})) |
- if new_files or new_dirs: |
- old_os = old_os.union([for_os.lstrip('!')]) |
- return union(old_files, new_files), union(old_dirs, new_dirs), old_os |
- |
- |
-def parse_gyp_dict(value): |
- """Parses a gyp dict as returned by eval_content(). |
- |
- |value| is the loaded dictionary that was defined in the gyp file. |
- |
- Returns a 3-tuple, where the first two items are the same as the items |
- returned by process_variable() in the same order, and the last item is a set |
- of strings of all OSs seen in the input dict. |
- |
- The expected format is strict, anything diverting from the format below will |
- fail: |
- { |
- 'variables': { |
- 'isolate_files': [ |
- ... |
- ], |
- 'isolate_dirs: [ |
- ... |
- ], |
- }, |
- 'conditions': [ |
- ['OS=="<os>"', { |
- 'variables': { |
- ... |
- }, |
- }, { # else |
- 'variables': { |
- ... |
- }, |
- }], |
- ... |
- ], |
- } |
- """ |
- assert isinstance(value, dict), value |
- VALID_ROOTS = ['variables', 'conditions'] |
- assert set(VALID_ROOTS).issuperset(set(value)), value.keys() |
- |
- # Global level variables. |
- oses = set() |
- files, dirs = process_variables(None, value.get('variables', {})) |
- |
- # OS specific variables. |
- conditions = value.get('conditions', []) |
- assert isinstance(conditions, list), conditions |
- for condition in conditions: |
- assert isinstance(condition, list), condition |
- assert 2 <= len(condition) <= 3, condition |
- m = re.match(r'OS==\"([a-z]+)\"', condition[0]) |
- assert m, condition[0] |
- condition_os = m.group(1) |
- |
- files, dirs, oses = _process_inner( |
- condition_os, condition[1], files, dirs, oses) |
- |
- if len(condition) == 3: |
- files, dirs, oses = _process_inner( |
- '!' + condition_os, condition[2], files, dirs, oses) |
- |
- # TODO(maruel): _expand_negative() should be called here, because otherwise |
- # the OSes the negative condition represents is lost once the gyps are merged. |
- # This cause an invalid expansion in reduce_inputs() call. |
- return files, dirs, oses |
- |
- |
-def parse_gyp_dicts(gyps): |
- """Parses each gyp file and returns the merged results. |
- |
- It only loads what parse_gyp_dict() can process. |
- |
- Return values: |
- files: dict(filename, set(OS where this filename is a dependency)) |
- dirs: dict(dirame, set(OS where this dirname is a dependency)) |
- oses: set(all the OSes referenced) |
- """ |
- files = {} |
- dirs = {} |
- oses = set() |
- for gyp in gyps: |
- with open(gyp, 'rb') as gyp_file: |
- content = gyp_file.read() |
- gyp_files, gyp_dirs, gyp_oses = parse_gyp_dict(eval_content(content)) |
- files = union(gyp_files, files) |
- dirs = union(gyp_dirs, dirs) |
- oses |= gyp_oses |
- return files, dirs, oses |
- |
- |
-def _expand_negative(items, oses): |
- """Converts all '!foo' value in the set by oses.difference('foo').""" |
- assert None not in oses and len(oses) >= 2, oses |
- for name in items: |
- if None in items[name]: |
- # Shortcut any item having None in their set. An item listed in None means |
- # the item is a dependency on all OSes. As such, there is no need to list |
- # any OS. |
- items[name] = set([None]) |
- continue |
- for neg in [o for o in items[name] if o.startswith('!')]: |
- # Replace it with the inverse. |
- items[name] = items[name].union(oses.difference([neg[1:]])) |
- items[name].remove(neg) |
- if items[name] == oses: |
- items[name] = set([None]) |
- |
- |
-def _compact_negative(items, oses): |
- """Converts all oses.difference('foo') to '!foo'. |
- |
- It is doing the reverse of _expand_negative(). |
- """ |
- assert None not in oses and len(oses) >= 3, oses |
- for name in items: |
- missing = oses.difference(items[name]) |
- if len(missing) == 1: |
- # Replace it with a negative. |
- items[name] = set(['!' + tuple(missing)[0]]) |
- |
- |
-def reduce_inputs(files, dirs, oses): |
- """Reduces the variables to their strictest minimum.""" |
- # Construct the inverse map first. |
- # Look at each individual file and directory, map where they are used and |
- # reconstruct the inverse dictionary. |
- # First, expands all '!' builders into the reverse. |
- # TODO(maruel): This is too late to call _expand_negative(). The exact list |
- # negative OSes condition it represents is lost at that point. |
- _expand_negative(files, oses) |
- _expand_negative(dirs, oses) |
- |
- # Do not convert back to negative if only 2 OSes were merged. It is easier to |
- # read this way. |
- if len(oses) > 2: |
- _compact_negative(files, oses) |
- _compact_negative(dirs, oses) |
- |
- return files, dirs |
- |
- |
-def convert_to_gyp(files, dirs): |
- """Regenerates back a gyp-like configuration dict from files and dirs |
- mappings. |
- |
- Sort the lists. |
- """ |
- # First, inverse the mapping to make it dict first. |
- config = {} |
- def to_cond(items, name): |
- for item, oses in items.iteritems(): |
- for cond_os in oses: |
- condition_values = config.setdefault( |
- None if cond_os is None else cond_os.lstrip('!'), |
- [{}, {}]) |
- # If condition is negative, use index 1, else use index 0. |
- condition_value = condition_values[int((cond_os or '').startswith('!'))] |
- # The list of items (files or dirs). Append the new item and keep the |
- # list sorted. |
- l = condition_value.setdefault('variables', {}).setdefault(name, []) |
- l.append(item) |
- l.sort() |
- |
- to_cond(files, 'isolate_files') |
- to_cond(dirs, 'isolate_dirs') |
- |
- out = {} |
- for o in sorted(config): |
- d = config[o] |
- if o is None: |
- assert not d[1] |
- out = union(out, d[0]) |
- else: |
- c = out.setdefault('conditions', []) |
- if d[1]: |
- c.append(['OS=="%s"' % o] + d) |
- else: |
- c.append(['OS=="%s"' % o] + d[0:1]) |
- return out |
- |
- |
-def main(): |
- parser = optparse.OptionParser( |
- usage='%prog <options> [file1] [file2] ...') |
- parser.add_option( |
- '-v', '--verbose', action='count', default=0, help='Use multiple times') |
- |
- options, args = parser.parse_args() |
- level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] |
- logging.basicConfig( |
- level=level, |
- format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s') |
- |
- trace_inputs.pretty_print( |
- convert_to_gyp(*reduce_inputs(*parse_gyp_dicts(args))), |
- sys.stdout) |
- return 0 |
- |
- |
-if __name__ == '__main__': |
- sys.exit(main()) |