1#!/usr/bin/env python3
2#
3# See: go/camera-sideline#versions for more context
4#
5# This script generates the apex manifest version number (which is also used for
6# the outer aab/jar object's version available to PackageManager).
7#
8# That version is limited to INT_MAX
9# Strategy:
10#   if(local eng build)
11#      version = 2147480000
12#   else
13#      version = brach specific code + padding + numeric part of build number
14#
15# 2147480000 is chosen as being a value that can install over any expected build
16# server build number that is still a little smaller than INT_MAX to leave room
17# for maneuvering
18
19import os
20import re
21import sys
22
23BRANCH_SPECIFIC_VERSION_IDENTIFIER = 3  # sc-v2
24DEFAULT_ENG_BUILD_NUMBER = 2147480000
25DEFAULT_BAD_BUILD_NUMBER = DEFAULT_ENG_BUILD_NUMBER - 1
26
27
28def tame_box(numeric_build_number):
29  # Box the regex extracted value to a min and a max, just in case. Should not
30  # be needed ever.
31  if numeric_build_number < 1:
32    numeric_build_number = 1
33  if numeric_build_number > DEFAULT_ENG_BUILD_NUMBER:
34    numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
35  return numeric_build_number
36
37
38def get_version(input_path, output_path):
39
40  soong_build_number = DEFAULT_BAD_BUILD_NUMBER
41  numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
42
43  # Extract the Android Build ID from the build products
44  out_dir = os.getenv('OUT_DIR')
45  with open('%s/soong/build_number.txt' % out_dir, 'r') as file:
46    soong_build_number = file.read().replace('\n', '')
47
48  # Eng builds all have a default very high version number to permit for local
49  # builds to ovewrite whatever else is installed on the OS.
50  if soong_build_number.startswith('eng.'):  # eng.bills.20210901.235403
51    numeric_build_number = DEFAULT_ENG_BUILD_NUMBER
52  else:
53    # We only want the numeric part of the Android Build ID
54    match = re.search(r'([0-9]+)', soong_build_number)
55    if match:
56      numeric_build_number = int(match.group(0))
57    else:
58      numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
59
60  # Tame the inputs into a reasonable box, just in case
61  numeric_build_number = tame_box(numeric_build_number)
62  # print('numeric_build_number: %s' % str(numeric_build_number))
63
64  # With the numeric build number as a starting point, let's augment it with
65  # the BRANCH_SPECIFIC_VERSION_IDENTIFIER to differentiate build products from
66  # this branch according to: go/camera-sideline#versions
67  branched_build_number = numeric_build_number
68  if numeric_build_number == DEFAULT_ENG_BUILD_NUMBER:
69    # High default eng numbers can't really be multiplied so we add our branch
70    # specific number instead
71    branched_build_number = numeric_build_number + BRANCH_SPECIFIC_VERSION_IDENTIFIER
72  else:
73    # Two cases to consider:
74    #   1. A "regular" Android Build ID like: 7699287
75    #   2. A pending Android Build ID like: P25748464
76
77    # "It's just string concatenation"
78    string_build_number = '%s%s' % (BRANCH_SPECIFIC_VERSION_IDENTIFIER,
79                                    str(numeric_build_number).zfill(8))
80    # Ints in python3 are long
81    branched_build_number = int(string_build_number)
82    # Tame the result into a reasonable box, just in case
83    branched_build_number = tame_box(branched_build_number)
84
85  # print('soong_build_number: %s' % soong_build_number)
86  # print('branched_build_number: %s' % str(branched_build_number))
87
88  # Bash version:
89  # cat $1 | \
90  # sed -E "s/\{BUILD_NUMBER\}/$numeric_build_number/g" | \
91  # sed -E "s/\{BUILD_ID\}/$soong_build_number/g" > $2
92  try:
93    if os.path.exists(input_path):
94      input_fh = open(input_path, 'r')
95      contents = input_fh.readlines()
96      # print('Read: %s' % input_path)
97      new_contents = []
98      input_fh.close()
99      for line in contents:
100        line = line.replace('{BUILD_ID}', soong_build_number)
101        line = line.replace('{BUILD_NUMBER}', str(branched_build_number))
102        new_contents.append(line)
103      # print(new_contents)
104      output_fh = open(output_path, 'w')
105      output_fh.write(''.join(new_contents))
106      output_fh.close()
107      # print('Wrote: %s' % output_path)
108  except IOError:
109    print(f'error occurred trying to read the file {input_path}')
110    return 1
111
112  return 0
113
114
115def main():
116  return get_version(*sys.argv[1:])
117
118
119if __name__ == '__main__':
120  sys.exit(main())
121
122