| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| from __future__ import unicode_literals |
| |
| from collections import OrderedDict |
| from itertools import groupby |
| from operator import itemgetter |
| |
| WHITESPACE_CHARACTERS = ' \t' |
| |
| def indentation(line): |
| """Number of whitespace (tab and space) characters at start of |line|.""" |
| i = 0 |
| while i < len(line): |
| if line[i] not in WHITESPACE_CHARACTERS: |
| break |
| i += 1 |
| return i |
| |
| def dependency_map(lines): |
| """Return a dictionary with keys that are targets and values that |
| are ordered lists of targets that should also be built. |
| |
| This implementation is O(n^2), but lovely and simple! We walk the |
| targets in the list, and for each target we walk backwards |
| collecting its dependencies. To make the walking easier, we |
| reverse the list so that we are always walking forwards. |
| |
| """ |
| pairs = [(indentation(line), line.strip()) for line in lines] |
| pairs.reverse() |
| |
| deps = {} |
| |
| for i, (indent, target) in enumerate(pairs): |
| if not deps.has_key(target): |
| deps[target] = [] |
| |
| for j in range(i+1, len(pairs)): |
| ind, tar = pairs[j] |
| if ind < indent: |
| indent = ind |
| if tar not in deps[target]: |
| deps[target].append(tar) |
| |
| return deps |
| |
| def all_dependencies(*targets, **kwargs): |
| """Return a list containing |targets| and all the dependencies of |
| those targets. |
| |
| The relative order of targets is maintained if possible. |
| |
| """ |
| dm = kwargs.pop('dependency_map', None) |
| if dm is None: |
| dm = dependency_map(targets) |
| |
| all_targets = OrderedDict() # Used as an ordered set. |
| |
| for target in targets: |
| all_targets[target] = True |
| if target in dm: |
| for dependency in dm[target]: |
| # Move element back in the ordered set. |
| if dependency in all_targets: |
| del all_targets[dependency] |
| all_targets[dependency] = True |
| |
| return all_targets.keys() |
| |
| def add_extra_dependencies(target_pairs, dependency_map): |
| """Take a list [(make_dir, make_target)] and expand (make_dir, None) |
| entries with extra make dependencies from |dependency_map|. |
| |
| Returns an iterator of pairs (make_dir, make_target). |
| |
| """ |
| for make_target, group in groupby(target_pairs, itemgetter(1)): |
| # Return non-simple directory targets untouched. |
| if make_target is not None: |
| for pair in group: |
| yield pair |
| continue |
| |
| # Add extra dumbmake dependencies to simple directory targets. |
| make_dirs = [make_dir for make_dir, _ in group] |
| new_make_dirs = all_dependencies(*make_dirs, dependency_map=dependency_map) |
| |
| for make_dir in new_make_dirs: |
| yield make_dir, None |