xref: /aosp_15_r20/external/angle/build/util/version.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*8975f5c5SAndroid Build Coastguard Worker# Copyright 2014 The Chromium Authors
3*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker
6*8975f5c5SAndroid Build Coastguard Worker"""
7*8975f5c5SAndroid Build Coastguard Workerversion.py -- Chromium version string substitution utility.
8*8975f5c5SAndroid Build Coastguard Worker"""
9*8975f5c5SAndroid Build Coastguard Worker
10*8975f5c5SAndroid Build Coastguard Worker
11*8975f5c5SAndroid Build Coastguard Workerimport argparse
12*8975f5c5SAndroid Build Coastguard Workerimport os
13*8975f5c5SAndroid Build Coastguard Workerimport stat
14*8975f5c5SAndroid Build Coastguard Workerimport sys
15*8975f5c5SAndroid Build Coastguard Worker
16*8975f5c5SAndroid Build Coastguard Workerimport android_chrome_version
17*8975f5c5SAndroid Build Coastguard Worker
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Workerdef FetchValuesFromFile(values_dict, file_name):
20*8975f5c5SAndroid Build Coastguard Worker  """
21*8975f5c5SAndroid Build Coastguard Worker  Fetches KEYWORD=VALUE settings from the specified file.
22*8975f5c5SAndroid Build Coastguard Worker
23*8975f5c5SAndroid Build Coastguard Worker  Everything to the left of the first '=' is the keyword,
24*8975f5c5SAndroid Build Coastguard Worker  everything to the right is the value.  No stripping of
25*8975f5c5SAndroid Build Coastguard Worker  white space, so beware.
26*8975f5c5SAndroid Build Coastguard Worker
27*8975f5c5SAndroid Build Coastguard Worker  The file must exist, otherwise you get the Python exception from open().
28*8975f5c5SAndroid Build Coastguard Worker  """
29*8975f5c5SAndroid Build Coastguard Worker  with open(file_name, 'r') as f:
30*8975f5c5SAndroid Build Coastguard Worker    for line in f.readlines():
31*8975f5c5SAndroid Build Coastguard Worker      key, val = line.rstrip('\r\n').split('=', 1)
32*8975f5c5SAndroid Build Coastguard Worker      values_dict[key] = val
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Worker
35*8975f5c5SAndroid Build Coastguard Workerdef FetchValues(file_list, is_official_build=None):
36*8975f5c5SAndroid Build Coastguard Worker  """
37*8975f5c5SAndroid Build Coastguard Worker  Returns a dictionary of values to be used for substitution.
38*8975f5c5SAndroid Build Coastguard Worker
39*8975f5c5SAndroid Build Coastguard Worker  Populates the dictionary with KEYWORD=VALUE settings from the files in
40*8975f5c5SAndroid Build Coastguard Worker  'file_list'.
41*8975f5c5SAndroid Build Coastguard Worker
42*8975f5c5SAndroid Build Coastguard Worker  Explicitly adds the following value from internal calculations:
43*8975f5c5SAndroid Build Coastguard Worker
44*8975f5c5SAndroid Build Coastguard Worker    OFFICIAL_BUILD
45*8975f5c5SAndroid Build Coastguard Worker  """
46*8975f5c5SAndroid Build Coastguard Worker  CHROME_BUILD_TYPE = os.environ.get('CHROME_BUILD_TYPE')
47*8975f5c5SAndroid Build Coastguard Worker  if CHROME_BUILD_TYPE == '_official' or is_official_build:
48*8975f5c5SAndroid Build Coastguard Worker    official_build = '1'
49*8975f5c5SAndroid Build Coastguard Worker  else:
50*8975f5c5SAndroid Build Coastguard Worker    official_build = '0'
51*8975f5c5SAndroid Build Coastguard Worker
52*8975f5c5SAndroid Build Coastguard Worker  values = dict(
53*8975f5c5SAndroid Build Coastguard Worker    OFFICIAL_BUILD = official_build,
54*8975f5c5SAndroid Build Coastguard Worker  )
55*8975f5c5SAndroid Build Coastguard Worker
56*8975f5c5SAndroid Build Coastguard Worker  for file_name in file_list:
57*8975f5c5SAndroid Build Coastguard Worker    FetchValuesFromFile(values, file_name)
58*8975f5c5SAndroid Build Coastguard Worker
59*8975f5c5SAndroid Build Coastguard Worker  script_dirname = os.path.dirname(os.path.realpath(__file__))
60*8975f5c5SAndroid Build Coastguard Worker  if official_build == '1':
61*8975f5c5SAndroid Build Coastguard Worker    lastchange_filename = os.path.join(script_dirname, "LASTCHANGE")
62*8975f5c5SAndroid Build Coastguard Worker  else:
63*8975f5c5SAndroid Build Coastguard Worker    lastchange_filename = os.path.join(script_dirname, "LASTCHANGE.dummy")
64*8975f5c5SAndroid Build Coastguard Worker  lastchange_values = {}
65*8975f5c5SAndroid Build Coastguard Worker  FetchValuesFromFile(lastchange_values, lastchange_filename)
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Worker  for placeholder_key, placeholder_value in values.items():
68*8975f5c5SAndroid Build Coastguard Worker    values[placeholder_key] = SubstTemplate(placeholder_value,
69*8975f5c5SAndroid Build Coastguard Worker                                            lastchange_values)
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Worker  return values
72*8975f5c5SAndroid Build Coastguard Worker
73*8975f5c5SAndroid Build Coastguard Worker
74*8975f5c5SAndroid Build Coastguard Workerdef SubstTemplate(contents, values):
75*8975f5c5SAndroid Build Coastguard Worker  """
76*8975f5c5SAndroid Build Coastguard Worker  Returns the template with substituted values from the specified dictionary.
77*8975f5c5SAndroid Build Coastguard Worker
78*8975f5c5SAndroid Build Coastguard Worker  Keywords to be substituted are surrounded by '@':  @KEYWORD@.
79*8975f5c5SAndroid Build Coastguard Worker
80*8975f5c5SAndroid Build Coastguard Worker  No attempt is made to avoid recursive substitution.  The order
81*8975f5c5SAndroid Build Coastguard Worker  of evaluation is random based on the order of the keywords returned
82*8975f5c5SAndroid Build Coastguard Worker  by the Python dictionary.  So do NOT substitute a value that
83*8975f5c5SAndroid Build Coastguard Worker  contains any @KEYWORD@ strings expecting them to be recursively
84*8975f5c5SAndroid Build Coastguard Worker  substituted, okay?
85*8975f5c5SAndroid Build Coastguard Worker  """
86*8975f5c5SAndroid Build Coastguard Worker  for key, val in values.items():
87*8975f5c5SAndroid Build Coastguard Worker    try:
88*8975f5c5SAndroid Build Coastguard Worker      contents = contents.replace('@' + key + '@', val)
89*8975f5c5SAndroid Build Coastguard Worker    except TypeError:
90*8975f5c5SAndroid Build Coastguard Worker      print(repr(key), repr(val))
91*8975f5c5SAndroid Build Coastguard Worker  return contents
92*8975f5c5SAndroid Build Coastguard Worker
93*8975f5c5SAndroid Build Coastguard Worker
94*8975f5c5SAndroid Build Coastguard Workerdef SubstFile(file_name, values):
95*8975f5c5SAndroid Build Coastguard Worker  """
96*8975f5c5SAndroid Build Coastguard Worker  Returns the contents of the specified file_name with substituted values.
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Worker  Substituted values come from the specified dictionary.
99*8975f5c5SAndroid Build Coastguard Worker
100*8975f5c5SAndroid Build Coastguard Worker  This is like SubstTemplate, except it operates on a file.
101*8975f5c5SAndroid Build Coastguard Worker  """
102*8975f5c5SAndroid Build Coastguard Worker  with open(file_name, 'r') as f:
103*8975f5c5SAndroid Build Coastguard Worker    template = f.read()
104*8975f5c5SAndroid Build Coastguard Worker  return SubstTemplate(template, values)
105*8975f5c5SAndroid Build Coastguard Worker
106*8975f5c5SAndroid Build Coastguard Worker
107*8975f5c5SAndroid Build Coastguard Workerdef WriteIfChanged(file_name, contents, mode):
108*8975f5c5SAndroid Build Coastguard Worker  """
109*8975f5c5SAndroid Build Coastguard Worker  Writes the specified contents to the specified file_name.
110*8975f5c5SAndroid Build Coastguard Worker
111*8975f5c5SAndroid Build Coastguard Worker  Does nothing if the contents aren't different than the current contents.
112*8975f5c5SAndroid Build Coastguard Worker  """
113*8975f5c5SAndroid Build Coastguard Worker  try:
114*8975f5c5SAndroid Build Coastguard Worker    with open(file_name, 'r') as f:
115*8975f5c5SAndroid Build Coastguard Worker      old_contents = f.read()
116*8975f5c5SAndroid Build Coastguard Worker  except EnvironmentError:
117*8975f5c5SAndroid Build Coastguard Worker    pass
118*8975f5c5SAndroid Build Coastguard Worker  else:
119*8975f5c5SAndroid Build Coastguard Worker    if contents == old_contents and mode == stat.S_IMODE(
120*8975f5c5SAndroid Build Coastguard Worker        os.lstat(file_name).st_mode):
121*8975f5c5SAndroid Build Coastguard Worker      return
122*8975f5c5SAndroid Build Coastguard Worker    os.unlink(file_name)
123*8975f5c5SAndroid Build Coastguard Worker  with open(file_name, 'w') as f:
124*8975f5c5SAndroid Build Coastguard Worker    f.write(contents)
125*8975f5c5SAndroid Build Coastguard Worker  os.chmod(file_name, mode)
126*8975f5c5SAndroid Build Coastguard Worker
127*8975f5c5SAndroid Build Coastguard Worker
128*8975f5c5SAndroid Build Coastguard Workerdef BuildParser():
129*8975f5c5SAndroid Build Coastguard Worker  """Build argparse parser, with added arguments."""
130*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
131*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('-f', '--file', action='append', default=[],
132*8975f5c5SAndroid Build Coastguard Worker                      help='Read variables from FILE.')
133*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('-i', '--input', default=None,
134*8975f5c5SAndroid Build Coastguard Worker                      help='Read strings to substitute from FILE.')
135*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('-o', '--output', default=None,
136*8975f5c5SAndroid Build Coastguard Worker                      help='Write substituted strings to FILE.')
137*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('-t', '--template', default=None,
138*8975f5c5SAndroid Build Coastguard Worker                      help='Use TEMPLATE as the strings to substitute.')
139*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('-x',
140*8975f5c5SAndroid Build Coastguard Worker                      '--executable',
141*8975f5c5SAndroid Build Coastguard Worker                      default=False,
142*8975f5c5SAndroid Build Coastguard Worker                      action='store_true',
143*8975f5c5SAndroid Build Coastguard Worker                      help='Set the executable bit on the output (on POSIX).')
144*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
145*8975f5c5SAndroid Build Coastguard Worker      '-e',
146*8975f5c5SAndroid Build Coastguard Worker      '--eval',
147*8975f5c5SAndroid Build Coastguard Worker      action='append',
148*8975f5c5SAndroid Build Coastguard Worker      default=[],
149*8975f5c5SAndroid Build Coastguard Worker      help='Evaluate VAL after reading variables. Can be used '
150*8975f5c5SAndroid Build Coastguard Worker      'to synthesize variables. e.g. -e \'PATCH_HI=int('
151*8975f5c5SAndroid Build Coastguard Worker      'PATCH)//256.')
152*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument(
153*8975f5c5SAndroid Build Coastguard Worker      '-a',
154*8975f5c5SAndroid Build Coastguard Worker      '--arch',
155*8975f5c5SAndroid Build Coastguard Worker      default=None,
156*8975f5c5SAndroid Build Coastguard Worker      choices=android_chrome_version.ARCH_CHOICES,
157*8975f5c5SAndroid Build Coastguard Worker      help='Set which cpu architecture the build is for.')
158*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--os', default=None, help='Set the target os.')
159*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--official', action='store_true',
160*8975f5c5SAndroid Build Coastguard Worker                      help='Whether the current build should be an official '
161*8975f5c5SAndroid Build Coastguard Worker                           'build, used in addition to the environment '
162*8975f5c5SAndroid Build Coastguard Worker                           'variable.')
163*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--next',
164*8975f5c5SAndroid Build Coastguard Worker                      action='store_true',
165*8975f5c5SAndroid Build Coastguard Worker                      help='Whether the current build should be a "next" '
166*8975f5c5SAndroid Build Coastguard Worker                      'build, which targets pre-release versions of Android.')
167*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('args', nargs=argparse.REMAINDER,
168*8975f5c5SAndroid Build Coastguard Worker                      help='For compatibility: INPUT and OUTPUT can be '
169*8975f5c5SAndroid Build Coastguard Worker                           'passed as positional arguments.')
170*8975f5c5SAndroid Build Coastguard Worker  return parser
171*8975f5c5SAndroid Build Coastguard Worker
172*8975f5c5SAndroid Build Coastguard Worker
173*8975f5c5SAndroid Build Coastguard Workerdef BuildEvals(options, parser):
174*8975f5c5SAndroid Build Coastguard Worker  """Construct a dict of passed '-e' arguments for evaluating."""
175*8975f5c5SAndroid Build Coastguard Worker  evals = {}
176*8975f5c5SAndroid Build Coastguard Worker  for expression in options.eval:
177*8975f5c5SAndroid Build Coastguard Worker    try:
178*8975f5c5SAndroid Build Coastguard Worker      evals.update(dict([expression.split('=', 1)]))
179*8975f5c5SAndroid Build Coastguard Worker    except ValueError:
180*8975f5c5SAndroid Build Coastguard Worker      parser.error('-e requires VAR=VAL')
181*8975f5c5SAndroid Build Coastguard Worker  return evals
182*8975f5c5SAndroid Build Coastguard Worker
183*8975f5c5SAndroid Build Coastguard Worker
184*8975f5c5SAndroid Build Coastguard Workerdef ModifyOptionsCompat(options, parser):
185*8975f5c5SAndroid Build Coastguard Worker  """Support compatibility with old versions.
186*8975f5c5SAndroid Build Coastguard Worker
187*8975f5c5SAndroid Build Coastguard Worker  Specifically, for old versions that considered the first two
188*8975f5c5SAndroid Build Coastguard Worker  positional arguments shorthands for --input and --output.
189*8975f5c5SAndroid Build Coastguard Worker  """
190*8975f5c5SAndroid Build Coastguard Worker  while len(options.args) and (options.input is None or options.output is None):
191*8975f5c5SAndroid Build Coastguard Worker    if options.input is None:
192*8975f5c5SAndroid Build Coastguard Worker      options.input = options.args.pop(0)
193*8975f5c5SAndroid Build Coastguard Worker    elif options.output is None:
194*8975f5c5SAndroid Build Coastguard Worker      options.output = options.args.pop(0)
195*8975f5c5SAndroid Build Coastguard Worker  if options.args:
196*8975f5c5SAndroid Build Coastguard Worker    parser.error('Unexpected arguments: %r' % options.args)
197*8975f5c5SAndroid Build Coastguard Worker
198*8975f5c5SAndroid Build Coastguard Worker
199*8975f5c5SAndroid Build Coastguard Workerdef GenerateValues(options, evals):
200*8975f5c5SAndroid Build Coastguard Worker  """Construct a dict of raw values used to generate output.
201*8975f5c5SAndroid Build Coastguard Worker
202*8975f5c5SAndroid Build Coastguard Worker  e.g. this could return a dict like
203*8975f5c5SAndroid Build Coastguard Worker  {
204*8975f5c5SAndroid Build Coastguard Worker    'BUILD': 74,
205*8975f5c5SAndroid Build Coastguard Worker  }
206*8975f5c5SAndroid Build Coastguard Worker
207*8975f5c5SAndroid Build Coastguard Worker  which would be used to resolve a template like
208*8975f5c5SAndroid Build Coastguard Worker  'build = "@BUILD@"' into 'build = "74"'
209*8975f5c5SAndroid Build Coastguard Worker
210*8975f5c5SAndroid Build Coastguard Worker  """
211*8975f5c5SAndroid Build Coastguard Worker  values = FetchValues(options.file, options.official)
212*8975f5c5SAndroid Build Coastguard Worker
213*8975f5c5SAndroid Build Coastguard Worker  for key, val in evals.items():
214*8975f5c5SAndroid Build Coastguard Worker    values[key] = str(eval(val, globals(), values))
215*8975f5c5SAndroid Build Coastguard Worker
216*8975f5c5SAndroid Build Coastguard Worker  if options.os == 'android':
217*8975f5c5SAndroid Build Coastguard Worker    android_chrome_version_codes = android_chrome_version.GenerateVersionCodes(
218*8975f5c5SAndroid Build Coastguard Worker        int(values['BUILD']), int(values['PATCH']), options.arch, options.next)
219*8975f5c5SAndroid Build Coastguard Worker    values.update(android_chrome_version_codes)
220*8975f5c5SAndroid Build Coastguard Worker
221*8975f5c5SAndroid Build Coastguard Worker  return values
222*8975f5c5SAndroid Build Coastguard Worker
223*8975f5c5SAndroid Build Coastguard Worker
224*8975f5c5SAndroid Build Coastguard Workerdef GenerateOutputContents(options, values):
225*8975f5c5SAndroid Build Coastguard Worker  """Construct output string (e.g. from template).
226*8975f5c5SAndroid Build Coastguard Worker
227*8975f5c5SAndroid Build Coastguard Worker  Arguments:
228*8975f5c5SAndroid Build Coastguard Worker  options -- argparse parsed arguments
229*8975f5c5SAndroid Build Coastguard Worker  values -- dict with raw values used to resolve the keywords in a template
230*8975f5c5SAndroid Build Coastguard Worker    string
231*8975f5c5SAndroid Build Coastguard Worker  """
232*8975f5c5SAndroid Build Coastguard Worker
233*8975f5c5SAndroid Build Coastguard Worker  if options.template is not None:
234*8975f5c5SAndroid Build Coastguard Worker    return SubstTemplate(options.template, values)
235*8975f5c5SAndroid Build Coastguard Worker  elif options.input:
236*8975f5c5SAndroid Build Coastguard Worker    return SubstFile(options.input, values)
237*8975f5c5SAndroid Build Coastguard Worker  else:
238*8975f5c5SAndroid Build Coastguard Worker    # Generate a default set of version information.
239*8975f5c5SAndroid Build Coastguard Worker    return """MAJOR=%(MAJOR)s
240*8975f5c5SAndroid Build Coastguard WorkerMINOR=%(MINOR)s
241*8975f5c5SAndroid Build Coastguard WorkerBUILD=%(BUILD)s
242*8975f5c5SAndroid Build Coastguard WorkerPATCH=%(PATCH)s
243*8975f5c5SAndroid Build Coastguard WorkerLASTCHANGE=%(LASTCHANGE)s
244*8975f5c5SAndroid Build Coastguard WorkerOFFICIAL_BUILD=%(OFFICIAL_BUILD)s
245*8975f5c5SAndroid Build Coastguard Worker""" % values
246*8975f5c5SAndroid Build Coastguard Worker
247*8975f5c5SAndroid Build Coastguard Worker
248*8975f5c5SAndroid Build Coastguard Workerdef GenerateOutputMode(options):
249*8975f5c5SAndroid Build Coastguard Worker  """Construct output mode (e.g. from template).
250*8975f5c5SAndroid Build Coastguard Worker
251*8975f5c5SAndroid Build Coastguard Worker  Arguments:
252*8975f5c5SAndroid Build Coastguard Worker  options -- argparse parsed arguments
253*8975f5c5SAndroid Build Coastguard Worker  """
254*8975f5c5SAndroid Build Coastguard Worker  if options.executable:
255*8975f5c5SAndroid Build Coastguard Worker    return 0o755
256*8975f5c5SAndroid Build Coastguard Worker  else:
257*8975f5c5SAndroid Build Coastguard Worker    return 0o644
258*8975f5c5SAndroid Build Coastguard Worker
259*8975f5c5SAndroid Build Coastguard Worker
260*8975f5c5SAndroid Build Coastguard Workerdef BuildOutput(args):
261*8975f5c5SAndroid Build Coastguard Worker  """Gets all input and output values needed for writing output."""
262*8975f5c5SAndroid Build Coastguard Worker  # Build argparse parser with arguments
263*8975f5c5SAndroid Build Coastguard Worker  parser = BuildParser()
264*8975f5c5SAndroid Build Coastguard Worker  options = parser.parse_args(args)
265*8975f5c5SAndroid Build Coastguard Worker
266*8975f5c5SAndroid Build Coastguard Worker  # Get dict of passed '-e' arguments for evaluating
267*8975f5c5SAndroid Build Coastguard Worker  evals = BuildEvals(options, parser)
268*8975f5c5SAndroid Build Coastguard Worker  # For compatibility with interface that considered first two positional
269*8975f5c5SAndroid Build Coastguard Worker  # arguments shorthands for --input and --output.
270*8975f5c5SAndroid Build Coastguard Worker  ModifyOptionsCompat(options, parser)
271*8975f5c5SAndroid Build Coastguard Worker
272*8975f5c5SAndroid Build Coastguard Worker  # Get the raw values that will be used the generate the output
273*8975f5c5SAndroid Build Coastguard Worker  values = GenerateValues(options, evals)
274*8975f5c5SAndroid Build Coastguard Worker  # Get the output string and mode
275*8975f5c5SAndroid Build Coastguard Worker  contents = GenerateOutputContents(options, values)
276*8975f5c5SAndroid Build Coastguard Worker  mode = GenerateOutputMode(options)
277*8975f5c5SAndroid Build Coastguard Worker
278*8975f5c5SAndroid Build Coastguard Worker  return {'options': options, 'contents': contents, 'mode': mode}
279*8975f5c5SAndroid Build Coastguard Worker
280*8975f5c5SAndroid Build Coastguard Worker
281*8975f5c5SAndroid Build Coastguard Workerdef main(args):
282*8975f5c5SAndroid Build Coastguard Worker  output = BuildOutput(args)
283*8975f5c5SAndroid Build Coastguard Worker
284*8975f5c5SAndroid Build Coastguard Worker  if output['options'].output is not None:
285*8975f5c5SAndroid Build Coastguard Worker    WriteIfChanged(output['options'].output, output['contents'], output['mode'])
286*8975f5c5SAndroid Build Coastguard Worker  else:
287*8975f5c5SAndroid Build Coastguard Worker    print(output['contents'])
288*8975f5c5SAndroid Build Coastguard Worker
289*8975f5c5SAndroid Build Coastguard Worker  return 0
290*8975f5c5SAndroid Build Coastguard Worker
291*8975f5c5SAndroid Build Coastguard Worker
292*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
293*8975f5c5SAndroid Build Coastguard Worker  sys.exit(main(sys.argv[1:]))
294