BUILD_simulator.py 2.9 KB

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