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