• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2014 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7version.py -- Chromium version string substitution utility.
8"""
9
10import argparse
11import os
12import sys
13
14
15def fetch_values_from_file(values_dict, file_name):
16  """
17  Fetches KEYWORD=VALUE settings from the specified file.
18
19  Everything to the left of the first '=' is the keyword,
20  everything to the right is the value.  No stripping of
21  white space, so beware.
22
23  The file must exist, otherwise you get the Python exception from open().
24  """
25  for line in open(file_name, 'r').readlines():
26    key, val = line.rstrip('\r\n').split('=', 1)
27    values_dict[key] = val
28
29
30def fetch_values(file_list):
31  """
32  Returns a dictionary of values to be used for substitution, populating
33  the dictionary with KEYWORD=VALUE settings from the files in 'file_list'.
34
35  Explicitly adds the following value from internal calculations:
36
37    OFFICIAL_BUILD
38  """
39  CHROME_BUILD_TYPE = os.environ.get('CHROME_BUILD_TYPE')
40  if CHROME_BUILD_TYPE == '_official':
41    official_build = '1'
42  else:
43    official_build = '0'
44
45  values = dict(
46    OFFICIAL_BUILD = official_build,
47  )
48
49  for file_name in file_list:
50    fetch_values_from_file(values, file_name)
51
52  return values
53
54
55def subst_template(contents, values):
56  """
57  Returns the template with substituted values from the specified dictionary.
58
59  Keywords to be substituted are surrounded by '@':  @KEYWORD@.
60
61  No attempt is made to avoid recursive substitution.  The order
62  of evaluation is random based on the order of the keywords returned
63  by the Python dictionary.  So do NOT substitute a value that
64  contains any @KEYWORD@ strings expecting them to be recursively
65  substituted, okay?
66  """
67  for key, val in values.iteritems():
68    try:
69      contents = contents.replace('@' + key + '@', val)
70    except TypeError:
71      print repr(key), repr(val)
72  return contents
73
74
75def subst_file(file_name, values):
76  """
77  Returns the contents of the specified file_name with substituted
78  values from the specified dictionary.
79
80  This is like subst_template, except it operates on a file.
81  """
82  template = open(file_name, 'r').read()
83  return subst_template(template, values);
84
85
86def write_if_changed(file_name, contents):
87  """
88  Writes the specified contents to the specified file_name
89  iff the contents are different than the current contents.
90  """
91  try:
92    old_contents = open(file_name, 'r').read()
93  except EnvironmentError:
94    pass
95  else:
96    if contents == old_contents:
97      return
98    os.unlink(file_name)
99  open(file_name, 'w').write(contents)
100
101
102def main():
103  parser = argparse.ArgumentParser()
104  parser.add_argument('-f', '--file', action='append', default=[],
105                      help='Read variables from FILE.')
106  parser.add_argument('-i', '--input', default=None,
107                      help='Read strings to substitute from FILE.')
108  parser.add_argument('-o', '--output', default=None,
109                      help='Write substituted strings to FILE.')
110  parser.add_argument('-t', '--template', default=None,
111                      help='Use TEMPLATE as the strings to substitute.')
112  parser.add_argument('-e', '--eval', action='append', default=[],
113                      help='Evaluate VAL after reading variables. Can be used '
114                           'to synthesize variables. e.g. -e \'PATCH_HI=int('
115                           'PATCH)/256.')
116  parser.add_argument('args', nargs=argparse.REMAINDER,
117                      help='For compatibility: INPUT and OUTPUT can be '
118                           'passed as positional arguments.')
119  options = parser.parse_args()
120
121  evals = {}
122  for expression in options.eval:
123    try:
124      evals.update(dict([expression.split('=', 1)]))
125    except ValueError:
126      parser.error('-e requires VAR=VAL')
127
128  # Compatibility with old versions that considered the first two positional
129  # arguments shorthands for --input and --output.
130  while len(options.args) and (options.input is None or \
131                               options.output is None):
132    if options.input is None:
133      options.input = options.args.pop(0)
134    elif options.output is None:
135      options.output = options.args.pop(0)
136  if options.args:
137    parser.error('Unexpected arguments: %r' % options.args)
138
139  values = fetch_values(options.file)
140  for key, val in evals.iteritems():
141    values[key] = str(eval(val, globals(), values))
142
143  if options.template is not None:
144    contents = subst_template(options.template, values)
145  elif options.input:
146    contents = subst_file(options.input, values)
147  else:
148    # Generate a default set of version information.
149    contents = """MAJOR=%(MAJOR)s
150MINOR=%(MINOR)s
151BUILD=%(BUILD)s
152PATCH=%(PATCH)s
153LASTCHANGE=%(LASTCHANGE)s
154OFFICIAL_BUILD=%(OFFICIAL_BUILD)s
155""" % values
156
157  if options.output is not None:
158    write_if_changed(options.output, contents)
159  else:
160    print contents
161
162  return 0
163
164
165if __name__ == '__main__':
166  sys.exit(main())
167