Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2015 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 # This script does a very rough simulation of BUILD file expansion,
      9 # mostly to see the effects of glob().
     10 
     11 # We start by adding some symbols to our namespace that BUILD.public calls.
     12 
     13 import glob
     14 import os
     15 import pprint
     16 import re
     17 
     18 def noop(*args, **kwargs):
     19   pass
     20 
     21 def select_simulator(d):
     22   result = []
     23   for k in d:
     24     result.append("*** BEGIN %s ***" % k)
     25     result.extend(d[k])
     26     result.append("*** END %s ***" % k)
     27   return result
     28 
     29 DOUBLE_STAR_RE = re.compile(r'/\*\*/')
     30 STAR_RE = re.compile(r'\*')
     31 DOUBLE_STAR_PLACEHOLDER = "xxxdoublestarxxx"
     32 STAR_PLACEHOLDER = "xxxstarxxx"
     33 
     34 # Returns a set of files that match pattern.
     35 def BUILD_glob_single(pattern):
     36   if pattern.find('**') < 0:
     37     # If pattern doesn't include **, glob.glob more-or-less does the right
     38     # thing.
     39     return glob.glob(pattern)
     40   # First transform pattern into a regexp.
     41   # Temporarily remove ** and *.
     42   pattern2 = DOUBLE_STAR_RE.sub(DOUBLE_STAR_PLACEHOLDER, pattern)
     43   pattern3 = STAR_RE.sub(STAR_PLACEHOLDER, pattern2)
     44   # Replace any regexp special characters.
     45   pattern4 = re.escape(pattern3)
     46   # Replace * with [^/]* and ** with .*.
     47   pattern5 = pattern4.replace(STAR_PLACEHOLDER, '[^/]*')
     48   pattern6 = pattern5.replace(DOUBLE_STAR_PLACEHOLDER, '.*/')
     49   # Anchor the match at the beginning and end.
     50   pattern7 = "^" + pattern6 + "$"
     51   pattern_re = re.compile(pattern7)
     52   matches = set()
     53   for root, _, files in os.walk('.'):
     54     for fname in files:
     55       # Remove initial "./".
     56       path = os.path.join(root, fname)[2:]
     57       if pattern_re.match(path):
     58         matches.add(path)
     59   return matches
     60 
     61 # Simulates BUILD file glob().
     62 def BUILD_glob(include, exclude=()):
     63   files = set()
     64   for pattern in include:
     65     files.update(BUILD_glob_single(pattern))
     66   for pattern in exclude:
     67     files.difference_update(BUILD_glob_single(pattern))
     68   return list(sorted(files))
     69 
     70 # With these namespaces, we can treat BUILD.public as if it were
     71 # Python code.  This pulls its variable definitions (SRCS, HDRS,
     72 # DEFINES, etc.) into local_names.
     73 global_names = {
     74   'cc_library': noop,
     75   'cc_test': noop,
     76   'exports_files': noop,
     77   'glob': BUILD_glob,
     78   'select': select_simulator,
     79   'BASE_DIR': '',
     80   'BASE_EXTERNAL_DEPS_ANDROID': [],
     81   'BASE_EXTERNAL_DEPS_IOS': [],
     82   'BASE_EXTERNAL_DEPS_UNIX': [],
     83   'CONDITION_ANDROID': 'CONDITION_ANDROID',
     84   'CONDITION_IOS': 'CONDITION_IOS',
     85   'DM_EXTERNAL_DEPS': [],
     86   'EXTERNAL_DEPS_ALL': [],
     87   'EXTERNAL_INCLUDES': [],
     88 }
     89 local_names = {}
     90 execfile('BUILD.public', global_names, local_names)
     91 
     92 with open('tools/BUILD.public.expected', 'w') as out:
     93   print >>out, "This file is auto-generated by tools/BUILD_simulator.py."
     94   print >>out, "It expands BUILD.public to make it easy to see changes."
     95   for name, value in sorted(local_names.items()):
     96     print >>out, name, '= ',
     97     pprint.pprint(value, out)
     98