• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Shared code for validating generated_file_staleness_test() rules.
2
3This code is used by test scripts generated from
4generated_file_staleness_test() rules.
5"""
6
7from __future__ import absolute_import
8from __future__ import print_function
9
10import sys
11import os
12from shutil import copyfile
13
14
15class _FilePair(object):
16  """Represents a single (target, generated) file pair."""
17
18  def __init__(self, target, generated):
19    self.target = target
20    self.generated = generated
21
22
23class Config(object):
24  """Represents the configuration for a single staleness test target."""
25
26  def __init__(self, file_list):
27    # Duplicate to avoid modifying our arguments.
28    file_list = list(file_list)
29
30    # The file list contains a few other bits of information at the end.
31    # This is packed by the code in build_defs.bzl.
32    self.target_name = file_list.pop()
33    self.package_name = file_list.pop()
34    self.pattern = file_list.pop()
35
36    self.file_list = file_list
37
38
39def _GetFilePairs(config):
40  """Generates the list of file pairs.
41
42  Args:
43    config: a Config object representing this target's config.
44
45  Returns:
46    A list of _FilePair objects.
47  """
48
49  ret = []
50
51  has_bazel_genfiles = os.path.exists("bazel-bin")
52
53  for filename in config.file_list:
54    target = os.path.join(config.package_name, filename)
55    generated = os.path.join(config.package_name, config.pattern % filename)
56    if has_bazel_genfiles:
57      generated = os.path.join("bazel-bin", generated)
58
59    # Generated files should always exist.  Blaze should guarantee this before
60    # we are run.
61    if not os.path.isfile(generated):
62      print("Generated file '%s' does not exist." % generated)
63      print("Please run this command to generate it:")
64      print("  bazel build %s:%s" % (config.package_name, config.target_name))
65      sys.exit(1)
66    ret.append(_FilePair(target, generated))
67
68  return ret
69
70
71def _GetMissingAndStaleFiles(file_pairs):
72  """Generates lists of missing and stale files.
73
74  Args:
75    file_pairs: a list of _FilePair objects.
76
77  Returns:
78    missing_files: a list of _FilePair objects representing missing files.
79      These target files do not exist at all.
80    stale_files: a list of _FilePair objects representing stale files.
81      These target files exist but have stale contents.
82  """
83
84  missing_files = []
85  stale_files = []
86
87  for pair in file_pairs:
88    if not os.path.isfile(pair.target):
89      missing_files.append(pair)
90      continue
91
92    with open(pair.generated) as g, open(pair.target) as t:
93      if g.read() != t.read():
94        stale_files.append(pair)
95
96  return missing_files, stale_files
97
98
99def _CopyFiles(file_pairs):
100  """Copies all generated files to the corresponding target file.
101
102  The target files must be writable already.
103
104  Args:
105    file_pairs: a list of _FilePair objects that we want to copy.
106  """
107
108  for pair in file_pairs:
109    target_dir = os.path.dirname(pair.target)
110    if not os.path.isdir(target_dir):
111      os.makedirs(target_dir)
112    copyfile(pair.generated, pair.target)
113
114
115def FixFiles(config):
116  """Implements the --fix option: overwrites missing or out-of-date files.
117
118  Args:
119    config: the Config object for this test.
120  """
121
122  file_pairs = _GetFilePairs(config)
123  missing_files, stale_files = _GetMissingAndStaleFiles(file_pairs)
124
125  _CopyFiles(stale_files + missing_files)
126
127
128def CheckFilesMatch(config):
129  """Checks whether each target file matches the corresponding generated file.
130
131  Args:
132    config: the Config object for this test.
133
134  Returns:
135    None if everything matches, otherwise a string error message.
136  """
137
138  diff_errors = []
139
140  file_pairs = _GetFilePairs(config)
141  missing_files, stale_files = _GetMissingAndStaleFiles(file_pairs)
142
143  for pair in missing_files:
144    diff_errors.append("File %s does not exist" % pair.target)
145    continue
146
147  for pair in stale_files:
148    diff_errors.append("File %s is out of date" % pair.target)
149
150  if diff_errors:
151    error_msg = "Files out of date!\n\n"
152    error_msg += "To fix run THIS command:\n"
153    error_msg += "  bazel-bin/%s/%s --fix\n\n" % (config.package_name,
154                                                  config.target_name)
155    error_msg += "Errors:\n"
156    error_msg += "  " + "\n  ".join(diff_errors)
157    return error_msg
158  else:
159    return None
160