#!/usr/bin/env python2 from __future__ import print_function import argparse import pprint import sys def die(msg): print(msg, file=sys.stderr) sys.exit(1) def do_unbundle(gypdata, targets): gyptargets = {t['target_name']: t for t in gypdata['targets']} dropped_deps = set() def _unbundle_in_block(gypblock): gypdeps = gypblock.get('dependencies') or {} for dep, (libs, defines) in unbundlings.items(): if dep not in gypdeps: continue gypdeps.remove(dep) try: ls = gyptarget['link_settings'] except KeyError: ls = gyptarget['link_settings'] = {} try: gyplibs = ls['libraries'] except KeyError: gyplibs = ls['libraries'] = [] gyplibs.extend('-l{}'.format(lib) for lib in libs) if defines: try: dd = gyptarget['defines'] except KeyError: dd = gyptarget['defines'] = [] dd.extend(defines) dropped_deps.add(dep) gypconds = gypblock.get('conditions') or [] for cond in gypconds: condblocks = cond[1:] for condblock in condblocks: _unbundle_in_block(condblock) for target, unbundlings in targets.items(): if target not in gyptargets: die('There is no {} target in gyp file'.format(target)) gyptarget = gyptargets[target] _unbundle_in_block(gyptarget) for gyptarget in gypdata['targets']: if gyptarget['target_name'] in dropped_deps: if gyptarget.get('dependencies'): dropped_deps.update(gyptarget.get('dependencies')) new_targets = [] for gyptarget in gypdata['targets']: if gyptarget['target_name'] not in dropped_deps: new_targets.append(gyptarget) gypdata['targets'] = new_targets gypconds = gypdata.get('conditions') if gypconds: for cond in gypconds: condblocks = cond[1:] for condblock in condblocks: new_targets = [] blocktargets = condblock.get('targets') if blocktargets: for blocktarget in blocktargets: if blocktarget['target_name'] not in dropped_deps: new_targets.append(blocktarget) condblock['targets'] = new_targets def main(): parser = argparse.ArgumentParser(description='Unbundle libs in gyp files') parser.add_argument('gypfile', type=str, help='input gyp file') parser.add_argument( '--unbundle', type=str, action='append', help='unbundle rule in the format ' ';;[;lib][;-DMACRO]') parser.add_argument( '-i', '--inplace', action='store_true', help='modify gyp file in-place') args = parser.parse_args() targets = {} for unbundle in args.unbundle: rule = list(filter(None, (i.strip() for i in unbundle.split(';')))) if len(rule) < 3: die('Invalid unbundle rule: {!r}'.format(unbundle)) target, dep = rule[:2] defines = [] libs = [] for item in rule[2:]: if item.startswith('-D'): defines.append(item[2:]) else: libs.append(item) try: target_unbundlings = targets[target] except KeyError: target_unbundlings = targets[target] = {} target_unbundlings[dep] = libs, defines with open(args.gypfile, 'rt') as f: gypdata = eval(f.read()) do_unbundle(gypdata, targets) if args.inplace: with open(args.gypfile, 'wt') as f: pprint.pprint(gypdata, stream=f) else: pprint.pprint(gypdata) if __name__ == '__main__': main()