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