117296607SReid Kleckner#!/usr/bin/env python3 217296607SReid Klecknerr"""Emulates the bits of CMake's configure_file() function needed in LLVM. 317296607SReid Kleckner 417296607SReid KlecknerThe CMake build uses configure_file() for several things. This emulates that 517296607SReid Klecknerfunction for the GN build. In the GN build, this runs at build time instead 617296607SReid Klecknerof at generator time. 717296607SReid Kleckner 817296607SReid KlecknerTakes a list of KEY=VALUE pairs (where VALUE can be empty). 917296607SReid Kleckner 1017296607SReid KlecknerThe sequence `\` `n` in each VALUE is replaced by a newline character. 1117296607SReid Kleckner 1217296607SReid KlecknerOn each line, replaces '${KEY}' or '@KEY@' with VALUE. 1317296607SReid Kleckner 1417296607SReid KlecknerThen, handles these special cases (note that FOO= sets the value of FOO to the 1517296607SReid Klecknerempty string, which is falsy, but FOO=0 sets it to '0' which is truthy): 1617296607SReid Kleckner 1717296607SReid Kleckner1.) #cmakedefine01 FOO 1817296607SReid Kleckner Checks if key FOO is set to a truthy value, and depending on that prints 1917296607SReid Kleckner one of the following two lines: 2017296607SReid Kleckner 2117296607SReid Kleckner #define FOO 1 2217296607SReid Kleckner #define FOO 0 2317296607SReid Kleckner 2417296607SReid Kleckner2.) #cmakedefine FOO [...] 2517296607SReid Kleckner Checks if key FOO is set to a truthy value, and depending on that prints 2617296607SReid Kleckner one of the following two lines: 2717296607SReid Kleckner 2817296607SReid Kleckner #define FOO [...] 2917296607SReid Kleckner /* #undef FOO */ 3017296607SReid Kleckner 3117296607SReid KlecknerFails if any of the KEY=VALUE arguments aren't needed for processing the 3217296607SReid Klecknerinput file, or if the input file references keys that weren't passed in. 3317296607SReid Kleckner""" 3417296607SReid Kleckner 3517296607SReid Klecknerimport argparse 3617296607SReid Klecknerimport os 3717296607SReid Klecknerimport re 3817296607SReid Klecknerimport sys 3917296607SReid Kleckner 4017296607SReid Kleckner 4117296607SReid Klecknerdef main(): 4217296607SReid Kleckner parser = argparse.ArgumentParser( 43*b71edfaaSTobias Hieta epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter 44*b71edfaaSTobias Hieta ) 45*b71edfaaSTobias Hieta parser.add_argument("input", help="input file") 46*b71edfaaSTobias Hieta parser.add_argument("values", nargs="*", help="several KEY=VALUE pairs") 47*b71edfaaSTobias Hieta parser.add_argument("-o", "--output", required=True, help="output file") 4817296607SReid Kleckner args = parser.parse_args() 4917296607SReid Kleckner 5017296607SReid Kleckner values = {} 5117296607SReid Kleckner for value in args.values: 52*b71edfaaSTobias Hieta key, val = value.split("=", 1) 5317296607SReid Kleckner if key in values: 5417296607SReid Kleckner print('duplicate key "%s" in args' % key, file=sys.stderr) 5517296607SReid Kleckner return 1 56*b71edfaaSTobias Hieta values[key] = val.replace("\\n", "\n") 5717296607SReid Kleckner unused_values = set(values.keys()) 5817296607SReid Kleckner 5917296607SReid Kleckner # Matches e.g. '${FOO}' or '@FOO@' and captures FOO in group 1 or 2. 60*b71edfaaSTobias Hieta var_re = re.compile(r"\$\{([^}]*)\}|@([^@]*)@") 6117296607SReid Kleckner 6217296607SReid Kleckner with open(args.input) as f: 6317296607SReid Kleckner in_lines = f.readlines() 6417296607SReid Kleckner out_lines = [] 6517296607SReid Kleckner for in_line in in_lines: 66*b71edfaaSTobias Hieta 6717296607SReid Kleckner def repl(m): 6817296607SReid Kleckner key = m.group(1) or m.group(2) 6917296607SReid Kleckner unused_values.discard(key) 7017296607SReid Kleckner return values[key] 71*b71edfaaSTobias Hieta 7217296607SReid Kleckner in_line = var_re.sub(repl, in_line) 73*b71edfaaSTobias Hieta if in_line.startswith("#cmakedefine01 "): 7417296607SReid Kleckner _, var = in_line.split() 75*b71edfaaSTobias Hieta if values[var] == "0": 7617296607SReid Kleckner print('error: "%s=0" used with #cmakedefine01 %s' % (var, var)) 7717296607SReid Kleckner print(" '0' evaluates as truthy with #cmakedefine01") 7817296607SReid Kleckner print(' use "%s=" instead' % var) 7917296607SReid Kleckner return 1 80*b71edfaaSTobias Hieta in_line = "#define %s %d\n" % (var, 1 if values[var] else 0) 8117296607SReid Kleckner unused_values.discard(var) 82*b71edfaaSTobias Hieta elif in_line.startswith("#cmakedefine "): 8317296607SReid Kleckner _, var = in_line.split(None, 1) 8417296607SReid Kleckner try: 8517296607SReid Kleckner var, val = var.split(None, 1) 86*b71edfaaSTobias Hieta in_line = "#define %s %s" % (var, val) # val ends in \n. 8717296607SReid Kleckner except: 8817296607SReid Kleckner var = var.rstrip() 89*b71edfaaSTobias Hieta in_line = "#define %s\n" % var 9017296607SReid Kleckner if not values[var]: 91*b71edfaaSTobias Hieta in_line = "/* #undef %s */\n" % var 9217296607SReid Kleckner unused_values.discard(var) 9317296607SReid Kleckner out_lines.append(in_line) 9417296607SReid Kleckner 9517296607SReid Kleckner if unused_values: 96*b71edfaaSTobias Hieta print("unused values args:", file=sys.stderr) 97*b71edfaaSTobias Hieta print(" " + "\n ".join(unused_values), file=sys.stderr) 9817296607SReid Kleckner return 1 9917296607SReid Kleckner 100*b71edfaaSTobias Hieta output = "".join(out_lines) 10117296607SReid Kleckner 10217296607SReid Kleckner leftovers = var_re.findall(output) 10317296607SReid Kleckner if leftovers: 10417296607SReid Kleckner print( 105*b71edfaaSTobias Hieta "unprocessed values:\n", 106*b71edfaaSTobias Hieta "\n".join([x[0] or x[1] for x in leftovers]), 107*b71edfaaSTobias Hieta file=sys.stderr, 108*b71edfaaSTobias Hieta ) 10917296607SReid Kleckner return 1 11017296607SReid Kleckner 11117296607SReid Kleckner def read(filename): 11217296607SReid Kleckner with open(args.output) as f: 11317296607SReid Kleckner return f.read() 11417296607SReid Kleckner 11517296607SReid Kleckner if not os.path.exists(args.output) or read(args.output) != output: 116*b71edfaaSTobias Hieta with open(args.output, "w") as f: 11717296607SReid Kleckner f.write(output) 11817296607SReid Kleckner os.chmod(args.output, os.stat(args.input).st_mode & 0o777) 11917296607SReid Kleckner 12017296607SReid Kleckner 121*b71edfaaSTobias Hietaif __name__ == "__main__": 12217296607SReid Kleckner sys.exit(main()) 123