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
13import glob
14import os
15import pprint
16import re
17
18def noop(*args, **kwargs):
19  pass
20
21def 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
29DOUBLE_STAR_RE = re.compile(r'/\*\*/')
30STAR_RE = re.compile(r'\*')
31DOUBLE_STAR_PLACEHOLDER = "xxxdoublestarxxx"
32STAR_PLACEHOLDER = "xxxstarxxx"
33
34# Returns a set of files that match pattern.
35def 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().
62def 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.
73global_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}
89local_names = {}
90execfile('BUILD.public', global_names, local_names)
91
92with 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