1#!/usr/bin/env python3 2 3import argparse 4import subprocess 5import shutil 6import sys 7from pathlib import Path 8import typing as T 9 10def run(argsv: T.List[str]) -> int: 11 commands = [[]] # type: T.List[T.List[str]] 12 SEPARATOR = ';;;' 13 14 # Generate CMD parameters 15 parser = argparse.ArgumentParser(description='Wrapper for add_custom_command') 16 parser.add_argument('-d', '--directory', type=str, metavar='D', required=True, help='Working directory to cwd to') 17 parser.add_argument('-o', '--outputs', nargs='+', metavar='O', required=True, help='Expected output files') 18 parser.add_argument('-O', '--original-outputs', nargs='*', metavar='O', default=[], help='Output files expected by CMake') 19 parser.add_argument('commands', nargs=argparse.REMAINDER, help=f'A "{SEPARATOR}" separated list of commands') 20 21 # Parse 22 args = parser.parse_args(argsv) 23 directory = Path(args.directory) 24 25 dummy_target = None 26 if len(args.outputs) == 1 and len(args.original_outputs) == 0: 27 dummy_target = Path(args.outputs[0]) 28 elif len(args.outputs) != len(args.original_outputs): 29 print('Length of output list and original output list differ') 30 return 1 31 32 for i in args.commands: 33 if i == SEPARATOR: 34 commands += [[]] 35 continue 36 37 i = i.replace('"', '') # Remove lefover quotes 38 commands[-1] += [i] 39 40 # Execute 41 for i in commands: 42 # Skip empty lists 43 if not i: 44 continue 45 46 cmd = [] 47 stdout = None 48 stderr = None 49 capture_file = '' 50 51 for j in i: 52 if j in ['>', '>>']: 53 stdout = subprocess.PIPE 54 continue 55 elif j in ['&>', '&>>']: 56 stdout = subprocess.PIPE 57 stderr = subprocess.STDOUT 58 continue 59 60 if stdout is not None or stderr is not None: 61 capture_file += j 62 else: 63 cmd += [j] 64 65 try: 66 directory.mkdir(parents=True, exist_ok=True) 67 68 res = subprocess.run(cmd, stdout=stdout, stderr=stderr, cwd=str(directory), check=True) 69 if capture_file: 70 out_file = directory / capture_file 71 out_file.write_bytes(res.stdout) 72 except subprocess.CalledProcessError: 73 return 1 74 75 if dummy_target: 76 dummy_target.touch() 77 return 0 78 79 # Copy outputs 80 zipped_outputs = zip([Path(x) for x in args.outputs], [Path(x) for x in args.original_outputs]) 81 for expected, generated in zipped_outputs: 82 do_copy = False 83 if not expected.exists(): 84 if not generated.exists(): 85 print('Unable to find generated file. This can cause the build to fail:') 86 print(generated) 87 do_copy = False 88 else: 89 do_copy = True 90 elif generated.exists(): 91 if generated.stat().st_mtime > expected.stat().st_mtime: 92 do_copy = True 93 94 if do_copy: 95 if expected.exists(): 96 expected.unlink() 97 shutil.copyfile(str(generated), str(expected)) 98 99 return 0 100 101if __name__ == '__main__': 102 sys.exit(run(sys.argv[1:])) 103