|  | #!/usr/bin/env python | 
|  | r"""Emulates the bits of CMake's configure_file() function needed in LLVM. | 
|  |  | 
|  | The CMake build uses configure_file() for several things.  This emulates that | 
|  | function for the GN build.  In the GN build, this runs at build time instead | 
|  | of at generator time. | 
|  |  | 
|  | Takes a list of KEY=VALUE pairs (where VALUE can be empty). | 
|  |  | 
|  | The sequence `\` `n` in each VALUE is replaced by a newline character. | 
|  |  | 
|  | On each line, replaces '${KEY}' or '@KEY@' with VALUE. | 
|  |  | 
|  | Then, handles these special cases (note that FOO= sets the value of FOO to the | 
|  | empty string, which is falsy, but FOO=0 sets it to '0' which is truthy): | 
|  |  | 
|  | 1.) #cmakedefine01 FOO | 
|  | Checks if key FOO is set to a truthy value, and depending on that prints | 
|  | one of the following two lines: | 
|  |  | 
|  | #define FOO 1 | 
|  | #define FOO 0 | 
|  |  | 
|  | 2.) #cmakedefine FOO [...] | 
|  | Checks if key FOO is set to a truthy value, and depending on that prints | 
|  | one of the following two lines: | 
|  |  | 
|  | #define FOO [...] | 
|  | /* #undef FOO */ | 
|  |  | 
|  | Fails if any of the KEY=VALUE arguments aren't needed for processing the | 
|  | input file, or if the input file references keys that weren't passed in. | 
|  | """ | 
|  |  | 
|  | from __future__ import print_function | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  | import re | 
|  | import sys | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser( | 
|  | epilog=__doc__, | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter) | 
|  | parser.add_argument('input', help='input file') | 
|  | parser.add_argument('values', nargs='*', help='several KEY=VALUE pairs') | 
|  | parser.add_argument('-o', '--output', required=True, | 
|  | help='output file') | 
|  | args = parser.parse_args() | 
|  |  | 
|  | values = {} | 
|  | for value in args.values: | 
|  | key, val = value.split('=', 1) | 
|  | if key in values: | 
|  | print('duplicate key "%s" in args' % key, file=sys.stderr) | 
|  | return 1 | 
|  | values[key] = val.replace('\\n', '\n') | 
|  | unused_values = set(values.keys()) | 
|  |  | 
|  | # Matches e.g. '${FOO}' or '@FOO@' and captures FOO in group 1 or 2. | 
|  | var_re = re.compile(r'\$\{([^}]*)\}|@([^@]*)@') | 
|  |  | 
|  | with open(args.input) as f: | 
|  | in_lines = f.readlines() | 
|  | out_lines = [] | 
|  | for in_line in in_lines: | 
|  | def repl(m): | 
|  | key = m.group(1) or m.group(2) | 
|  | unused_values.discard(key) | 
|  | return values[key] | 
|  | in_line = var_re.sub(repl, in_line) | 
|  | if in_line.startswith('#cmakedefine01 '): | 
|  | _, var = in_line.split() | 
|  | if values[var] == '0': | 
|  | print('error: "%s=0" used with #cmakedefine01 %s' % (var, var)) | 
|  | print("       '0' evaluates as truthy with #cmakedefine01") | 
|  | print('       use "%s=" instead' % var) | 
|  | return 1 | 
|  | in_line = '#define %s %d\n' % (var, 1 if values[var] else 0) | 
|  | unused_values.discard(var) | 
|  | elif in_line.startswith('#cmakedefine '): | 
|  | _, var = in_line.split(None, 1) | 
|  | try: | 
|  | var, val = var.split(None, 1) | 
|  | in_line = '#define %s %s' % (var, val)  # val ends in \n. | 
|  | except: | 
|  | var = var.rstrip() | 
|  | in_line = '#define %s\n' % var | 
|  | if not values[var]: | 
|  | in_line = '/* #undef %s */\n' % var | 
|  | unused_values.discard(var) | 
|  | out_lines.append(in_line) | 
|  |  | 
|  | if unused_values: | 
|  | print('unused values args:', file=sys.stderr) | 
|  | print('    ' + '\n    '.join(unused_values), file=sys.stderr) | 
|  | return 1 | 
|  |  | 
|  | output = ''.join(out_lines) | 
|  |  | 
|  | leftovers = var_re.findall(output) | 
|  | if leftovers: | 
|  | print( | 
|  | 'unprocessed values:\n', | 
|  | '\n'.join([x[0] or x[1] for x in leftovers]), | 
|  | file=sys.stderr) | 
|  | return 1 | 
|  |  | 
|  | def read(filename): | 
|  | with open(args.output) as f: | 
|  | return f.read() | 
|  |  | 
|  | if not os.path.exists(args.output) or read(args.output) != output: | 
|  | with open(args.output, 'w') as f: | 
|  | f.write(output) | 
|  | os.chmod(args.output, os.stat(args.input).st_mode & 0o777) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |