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