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