Changeset View
Changeset View
Standalone View
Standalone View
data/generators/generate-cmake-syntax.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
File Mode | null | 100755 |
1 | #!/usr/bin/env python3 | ||||
---|---|---|---|---|---|
2 | # -*- coding: utf-8 -*- | ||||
3 | # | ||||
4 | # Generate Kate syntax file for CMake | ||||
5 | # | ||||
6 | # Copyright (c) 2017, Alex Turbov <i.zaufi@gmail.com> | ||||
7 | # | ||||
8 | # To install prerequisites: | ||||
9 | # | ||||
10 | # $ pip install --user click jinja2 yaml | ||||
11 | # | ||||
12 | # To use: | ||||
13 | # | ||||
14 | # $ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml | ||||
15 | # | ||||
16 | import click | ||||
17 | import jinja2 | ||||
18 | import pathlib | ||||
19 | import re | ||||
20 | import yaml | ||||
21 | | ||||
22 | import pprint | ||||
23 | | ||||
24 | | ||||
25 | _TEMPLATED_NAME = re.compile('<[^>]+>') | ||||
26 | _PROPERTY_KEYS = [ | ||||
27 | 'global-properties' | ||||
28 | , 'directory-properties' | ||||
29 | , 'target-properties' | ||||
30 | , 'source-properties' | ||||
31 | , 'test-properties' | ||||
32 | , 'cache-properties' | ||||
33 | , 'install-properties' | ||||
34 | ] | ||||
35 | _KW_RE_LIST = ['kw', 're'] | ||||
36 | | ||||
37 | | ||||
38 | def try_transform_placeholder_string_to_regex(name): | ||||
39 | ''' | ||||
40 | NOTE Some placeholders are not IDs, but numbers... | ||||
41 | `CMAKE_MATCH_<N>` 4 example | ||||
42 | ''' | ||||
43 | m = _TEMPLATED_NAME.split(name) | ||||
44 | if 'CMAKE_MATCH_' in m: | ||||
45 | return '\\bCMAKE_MATCH_[0-9]+\\b' | ||||
46 | | ||||
47 | return '\\b{}\\b'.format('&id_re;'.join(list(m))) if 1 < len(m) else name | ||||
48 | | ||||
49 | | ||||
50 | def partition_iterable(fn, iterable): | ||||
51 | true, false = [], [] | ||||
52 | for i in iterable: | ||||
53 | (false, true)[int(fn(i))].append(i) | ||||
54 | return true, false | ||||
55 | | ||||
56 | | ||||
57 | def _transform_command_set(cmd, list_name): | ||||
58 | args, args_re = partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, cmd[list_name]) | ||||
59 | del cmd[list_name] | ||||
60 | list_name = list_name.replace('-', '_') | ||||
61 | | ||||
62 | cmd[list_name] = {k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [args, args_re])} | ||||
63 | cmd[list_name]['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), args_re)] | ||||
64 | | ||||
65 | return cmd | ||||
66 | | ||||
67 | | ||||
68 | def transform_command(cmd): | ||||
69 | can_be_nulary = True | ||||
70 | | ||||
71 | if 'name' not in cmd: | ||||
72 | raise RuntimeError('Command have no name') | ||||
73 | | ||||
74 | if 'named-args' in cmd: | ||||
75 | new_cmd = _transform_command_set(cmd, 'named-args') | ||||
76 | assert new_cmd == cmd | ||||
77 | can_be_nulary = False | ||||
78 | | ||||
79 | if 'special-args' in cmd: | ||||
80 | new_cmd = _transform_command_set(cmd, 'special-args') | ||||
81 | assert new_cmd == cmd | ||||
82 | can_be_nulary = False | ||||
83 | | ||||
84 | if 'property-args' in cmd: | ||||
85 | new_cmd = _transform_command_set(cmd, 'property-args') | ||||
86 | assert new_cmd == cmd | ||||
87 | can_be_nulary = False | ||||
88 | | ||||
89 | cmd['nested_parentheses'] = cmd['nested-parentheses?'] if 'nested-parentheses?' in cmd else False | ||||
90 | | ||||
91 | if 'nulary?' in cmd and cmd['nulary?'] and not can_be_nulary: | ||||
92 | raise RuntimeError('Command `{}` w/ args declared nulary!?'.format(cmd['name'])) | ||||
93 | | ||||
94 | return cmd | ||||
95 | | ||||
96 | | ||||
97 | #BEGIN Jinja filters | ||||
98 | | ||||
99 | def cmd_is_nulary(cmd): | ||||
100 | assert not ('named-args' in cmd or 'special-args' in cmd or 'property-args' in cmd) | ||||
101 | return 'nulary?' in cmd and cmd['nulary?'] | ||||
102 | | ||||
103 | #END Jinja filters | ||||
104 | | ||||
105 | | ||||
106 | @click.command() | ||||
107 | @click.argument('input_yaml', type=click.File('r')) | ||||
108 | @click.argument('template', type=click.File('r'), default='./cmake.xml.tpl') | ||||
109 | def cli(input_yaml, template): | ||||
110 | data = yaml.load(input_yaml) | ||||
111 | | ||||
112 | # Partition `variables` list into "pure" words and regexes to match | ||||
113 | data['variables'] = { | ||||
114 | k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [*partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data['variables'])]) | ||||
115 | } | ||||
116 | data['variables']['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), data['variables']['re'])] | ||||
117 | | ||||
118 | # Transform properties and make all-properties list | ||||
119 | data['properties'] = {} | ||||
120 | for prop in _PROPERTY_KEYS: | ||||
121 | python_prop_list_name = prop.replace('-', '_') | ||||
122 | props, props_re = partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data[prop]) | ||||
123 | del data[prop] | ||||
124 | | ||||
125 | data['properties'][python_prop_list_name] = {k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [props, props_re])} | ||||
126 | data['properties'][python_prop_list_name]['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), props_re)] | ||||
127 | | ||||
128 | | ||||
129 | data['properties']['kinds'] = [*map(lambda name: name.replace('-', '_'), _PROPERTY_KEYS)] | ||||
130 | | ||||
131 | # Make all commands list | ||||
132 | data['commands'] = [*map(lambda cmd: transform_command(cmd), data['scripting-commands'] + data['project-commands'] + data['ctest-commands'])] | ||||
133 | | ||||
134 | data['generator_expressions'] = data['generator-expressions'] | ||||
135 | | ||||
136 | | ||||
137 | env = jinja2.Environment( | ||||
138 | keep_trailing_newline=True | ||||
139 | ) | ||||
140 | | ||||
141 | # Register convenience filters | ||||
142 | env.tests['nulary'] = cmd_is_nulary | ||||
143 | | ||||
144 | tpl = env.from_string(template.read()) | ||||
145 | result = tpl.render(data) | ||||
146 | print(result) | ||||
147 | | ||||
148 | | ||||
149 | if __name__ == '__main__': | ||||
150 | cli() | ||||
151 | # TODO Handle execptions and show errors |