Home | History | Annotate | Download | only in gn
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2016 Google Inc.
      4 #
      5 # Use of this source code is governed by a BSD-style license that can be
      6 # found in the LICENSE file.
      7 
      8 import collections
      9 import json
     10 import os
     11 import subprocess
     12 import sys
     13 
     14 # Finds all public sources in include directories then write them to skia.h.
     15 
     16 # Also write skia.h.deps, which Ninja uses to track dependencies. It's the
     17 # very same mechanism Ninja uses to know which .h files affect which .cpp files.
     18 
     19 gn              = sys.argv[1]
     20 absolute_source = sys.argv[2]
     21 skia_h          = sys.argv[3]
     22 include_dirs    = sys.argv[4:]
     23 
     24 absolute_source = os.path.normpath(absolute_source)
     25 
     26 include_dirs = [os.path.join(os.path.normpath(include_dir), '')
     27                 for include_dir in include_dirs]
     28 include_dirs.sort(key=len, reverse=True)
     29 
     30 gn_desc_cmd = [gn, 'desc', '.', '--root=%s' % absolute_source, '--format=json',
     31                '*']
     32 
     33 desc_json_txt = ''
     34 try:
     35   desc_json_txt = subprocess.check_output(gn_desc_cmd)
     36 except subprocess.CalledProcessError as e:
     37   print e.output
     38   raise
     39 
     40 desc_json = {}
     41 try:
     42   desc_json = json.loads(desc_json_txt)
     43 except ValueError:
     44   print desc_json_txt
     45   raise
     46 
     47 sources = set()
     48 
     49 for target in desc_json.itervalues():
     50   # We'll use `public` headers if they're listed, or pull them from `sources`
     51   # if not.  GN sneaks in a default "public": "*" into the JSON if you don't
     52   # set one explicitly.
     53   search_list = target.get('public')
     54   if search_list == '*':
     55     search_list = target.get('sources', [])
     56 
     57   for name in search_list:
     58     sources.add(os.path.join(absolute_source, os.path.normpath(name[2:])))
     59 
     60 Header = collections.namedtuple('Header', ['absolute', 'include'])
     61 headers = {}
     62 for source in sources:
     63   source_as_include = [source[len(include_dir):]
     64                        for include_dir in include_dirs
     65                        if source.startswith(include_dir)]
     66   if not source_as_include:
     67     continue
     68   statinfo = os.stat(source)
     69   key = str(statinfo.st_ino) + ':' + str(statinfo.st_dev)
     70   # On Windows os.stat st_ino is 0 until 3.3.4 and st_dev is 0 until 3.4.0.
     71   if key == '0:0':
     72     key = source
     73   include_path = source_as_include[0]
     74   if key not in headers or len(include_path) < len(headers[key].include):
     75     headers[key] = Header(source, include_path)
     76 
     77 headers = headers.values()
     78 headers.sort(key=lambda x: x.include)
     79 
     80 with open(skia_h, 'w') as f:
     81   f.write('// skia.h generated by GN.\n')
     82   f.write('#ifndef skia_h_DEFINED\n')
     83   f.write('#define skia_h_DEFINED\n')
     84   for header in headers:
     85     f.write('#include "' + header.include + '"\n')
     86   f.write('#endif//skia_h_DEFINED\n')
     87 
     88 with open(skia_h + '.deps', 'w') as f:
     89   f.write(skia_h + ':')
     90   for header in headers:
     91     f.write(' ' + header.absolute)
     92   f.write(' build.ninja.d')
     93   f.write('\n')
     94 
     95 # Temporary: during development this file wrote skia.h.d, not skia.h.deps,
     96 # and I think we have some bad versions of those files laying around.
     97 if os.path.exists(skia_h + '.d'):
     98   os.remove(skia_h + '.d')
     99