mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:32:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			121 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python3
 | |
| r"""Emulates the bits of CMake's configure_file() function needed in serenity.
 | |
| 
 | |
| 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.
 | |
| """
 | |
| 
 | |
| 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 ') or in_line.startswith("#    cmakedefine01"):
 | |
|             in_line = in_line.replace('#    cmakedefine01', '#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(filename) 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())
 | 
