blob: 87e9bdc8519755fffbd2ec0c63e5a1222d1c3acb [file] [log] [blame]
Keith Mok57baaaf2023-06-13 22:33:10 +00001#!/usr/bin/python3
Yu Shan63e24d72022-06-24 17:53:32 +00002
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""A script to generate Java files and CPP header files based on annotations in VehicleProperty.aidl
18
19 Need ANDROID_BUILD_TOP environmental variable to be set. This script will update
20 ChangeModeForVehicleProperty.h and AccessForVehicleProperty.h under generated_lib/cpp and
Tyler Trephana1828f92023-10-06 02:39:13 +000021 ChangeModeForVehicleProperty.java, AccessForVehicleProperty.java, EnumForVehicleProperty.java under generated_lib/java.
Yu Shan63e24d72022-06-24 17:53:32 +000022
23 Usage:
24 $ python generate_annotation_enums.py
25"""
Yu Shan41dd7f12023-06-07 13:25:43 -070026import argparse
27import filecmp
Yu Shan63e24d72022-06-24 17:53:32 +000028import os
29import re
30import sys
Yu Shan41dd7f12023-06-07 13:25:43 -070031import tempfile
Yu Shan63e24d72022-06-24 17:53:32 +000032
Yu Shan41dd7f12023-06-07 13:25:43 -070033PROP_AIDL_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl_property/android/hardware/' +
34 'automotive/vehicle/VehicleProperty.aidl')
35CHANGE_MODE_CPP_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/' +
36 'ChangeModeForVehicleProperty.h')
37ACCESS_CPP_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/' +
38 'AccessForVehicleProperty.h')
39CHANGE_MODE_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
40 'ChangeModeForVehicleProperty.java')
41ACCESS_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
42 'AccessForVehicleProperty.java')
Tyler Trephana1828f92023-10-06 02:39:13 +000043ENUM_JAVA_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/java/' +
44 'EnumForVehicleProperty.java')
Yu Shan7881e822023-12-15 13:12:01 -080045VERSION_CPP_FILE_PATH = ('hardware/interfaces/automotive/vehicle/aidl/generated_lib/cpp/' +
46 'VersionForVehicleProperty.h')
Yu Shan41dd7f12023-06-07 13:25:43 -070047SCRIPT_PATH = 'hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py'
Yu Shan63e24d72022-06-24 17:53:32 +000048
Yu Shan41dd7f12023-06-07 13:25:43 -070049TAB = ' '
50RE_ENUM_START = re.compile('\s*enum VehicleProperty \{')
51RE_ENUM_END = re.compile('\s*\}\;')
52RE_COMMENT_BEGIN = re.compile('\s*\/\*\*?')
53RE_COMMENT_END = re.compile('\s*\*\/')
54RE_CHANGE_MODE = re.compile('\s*\* @change_mode (\S+)\s*')
Yu Shan7881e822023-12-15 13:12:01 -080055RE_VERSION = re.compile('\s*\* @version (\S+)\s*')
Yu Shan41dd7f12023-06-07 13:25:43 -070056RE_ACCESS = re.compile('\s*\* @access (\S+)\s*')
Yu Shan8ddd65d2023-06-28 17:02:29 -070057RE_DATA_ENUM = re.compile('\s*\* @data_enum (\S+)\s*')
58RE_UNIT = re.compile('\s*\* @unit (\S+)\s+')
Yu Shan41dd7f12023-06-07 13:25:43 -070059RE_VALUE = re.compile('\s*(\w+)\s*=(.*)')
Yu Shan63e24d72022-06-24 17:53:32 +000060
61LICENSE = """/*
Tyler Trephana1828f92023-10-06 02:39:13 +000062 * Copyright (C) 2023 The Android Open Source Project
Yu Shan63e24d72022-06-24 17:53:32 +000063 *
64 * Licensed under the Apache License, Version 2.0 (the "License");
65 * you may not use this file except in compliance with the License.
66 * You may obtain a copy of the License at
67 *
68 * http://www.apache.org/licenses/LICENSE-2.0
69 *
70 * Unless required by applicable law or agreed to in writing, software
71 * distributed under the License is distributed on an "AS IS" BASIS,
72 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
73 * See the License for the specific language governing permissions and
74 * limitations under the License.
75 */
76
77/**
78 * DO NOT EDIT MANUALLY!!!
79 *
80 * Generated by tools/generate_annotation_enums.py.
81 */
82
Aaqib Ismail2e8915d2023-01-30 13:04:05 -080083// clang-format off
84
Yu Shan63e24d72022-06-24 17:53:32 +000085"""
86
Yu Shan7881e822023-12-15 13:12:01 -080087CHANGE_MODE_CPP_HEADER = """#pragma once
Yu Shan63e24d72022-06-24 17:53:32 +000088
89#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
90#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyChangeMode.h>
91
92#include <unordered_map>
93
94namespace aidl {
95namespace android {
96namespace hardware {
97namespace automotive {
98namespace vehicle {
99
100std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehicleProperty = {
101"""
102
Yu Shan7881e822023-12-15 13:12:01 -0800103CPP_FOOTER = """
Yu Shan63e24d72022-06-24 17:53:32 +0000104};
105
106} // namespace vehicle
107} // namespace automotive
108} // namespace hardware
109} // namespace android
110} // aidl
Yu Shan63e24d72022-06-24 17:53:32 +0000111"""
112
Yu Shan7881e822023-12-15 13:12:01 -0800113ACCESS_CPP_HEADER = """#pragma once
Yu Shan63e24d72022-06-24 17:53:32 +0000114
115#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
116#include <aidl/android/hardware/automotive/vehicle/VehiclePropertyAccess.h>
117
118#include <unordered_map>
119
120namespace aidl {
121namespace android {
122namespace hardware {
123namespace automotive {
124namespace vehicle {
125
126std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehicleProperty = {
127"""
128
Yu Shan7881e822023-12-15 13:12:01 -0800129VERSION_CPP_HEADER = """#pragma once
Yu Shan63e24d72022-06-24 17:53:32 +0000130
Yu Shan7881e822023-12-15 13:12:01 -0800131#include <aidl/android/hardware/automotive/vehicle/VehicleProperty.h>
Yu Shan63e24d72022-06-24 17:53:32 +0000132
Yu Shan7881e822023-12-15 13:12:01 -0800133#include <unordered_map>
134
135namespace aidl {
136namespace android {
137namespace hardware {
138namespace automotive {
139namespace vehicle {
140
141std::unordered_map<VehicleProperty, int32_t> VersionForVehicleProperty = {
Yu Shan63e24d72022-06-24 17:53:32 +0000142"""
143
144CHANGE_MODE_JAVA_HEADER = """package android.hardware.automotive.vehicle;
145
146import java.util.Map;
147
148public final class ChangeModeForVehicleProperty {
149
150 public static final Map<Integer, Integer> values = Map.ofEntries(
151"""
152
Yu Shan7881e822023-12-15 13:12:01 -0800153JAVA_FOOTER = """
Yu Shan63e24d72022-06-24 17:53:32 +0000154 );
155
156}
157"""
158
159ACCESS_JAVA_HEADER = """package android.hardware.automotive.vehicle;
160
161import java.util.Map;
162
163public final class AccessForVehicleProperty {
164
165 public static final Map<Integer, Integer> values = Map.ofEntries(
166"""
167
Tyler Trephana1828f92023-10-06 02:39:13 +0000168ENUM_JAVA_HEADER = """package android.hardware.automotive.vehicle;
169
170import java.util.List;
171import java.util.Map;
172
173public final class EnumForVehicleProperty {
174
175 public static final Map<Integer, List<Class<?>>> values = Map.ofEntries(
176"""
177
Yu Shan63e24d72022-06-24 17:53:32 +0000178
Yu Shan8ddd65d2023-06-28 17:02:29 -0700179class PropertyConfig:
180 """Represents one VHAL property definition in VehicleProperty.aidl."""
Yu Shan63e24d72022-06-24 17:53:32 +0000181
Yu Shan8ddd65d2023-06-28 17:02:29 -0700182 def __init__(self):
183 self.name = None
184 self.description = None
185 self.change_mode = None
186 self.access_modes = []
187 self.enum_types = []
188 self.unit_type = None
Yu Shan7881e822023-12-15 13:12:01 -0800189 self.version = None
Yu Shan63e24d72022-06-24 17:53:32 +0000190
Yu Shan8ddd65d2023-06-28 17:02:29 -0700191 def __repr__(self):
192 return self.__str__()
193
194 def __str__(self):
195 return ('PropertyConfig{{' +
196 'name: {}, description: {}, change_mode: {}, access_modes: {}, enum_types: {}' +
197 ', unit_type: {}}}').format(self.name, self.description, self.change_mode,
198 self.access_modes, self.enum_types, self.unit_type)
199
200
201class FileParser:
202
203 def __init__(self):
204 self.configs = None
205
206 def parseFile(self, input_file):
207 """Parses the input VehicleProperty.aidl file into a list of property configs."""
Yu Shan63e24d72022-06-24 17:53:32 +0000208 processing = False
209 in_comment = False
Yu Shan8ddd65d2023-06-28 17:02:29 -0700210 configs = []
211 config = None
212 with open(input_file, 'r') as f:
Yu Shan63e24d72022-06-24 17:53:32 +0000213 for line in f.readlines():
214 if RE_ENUM_START.match(line):
215 processing = True
Yu Shan63e24d72022-06-24 17:53:32 +0000216 elif RE_ENUM_END.match(line):
217 processing = False
218 if not processing:
219 continue
220 if RE_COMMENT_BEGIN.match(line):
221 in_comment = True
Yu Shan8ddd65d2023-06-28 17:02:29 -0700222 config = PropertyConfig()
223 description = ''
Yu Shan63e24d72022-06-24 17:53:32 +0000224 if RE_COMMENT_END.match(line):
225 in_comment = False
226 if in_comment:
Yu Shan8ddd65d2023-06-28 17:02:29 -0700227 if not config.description:
228 sline = line.strip()
229 # Skip the first line of comment
230 if sline.startswith('*'):
231 # Remove the '*'.
232 sline = sline[1:].strip()
233 # We reach an empty line of comment, the description part is ending.
234 if sline == '':
235 config.description = description
236 else:
237 if description != '':
238 description += ' '
239 description += sline
240 match = RE_CHANGE_MODE.match(line)
241 if match:
242 config.change_mode = match.group(1).replace('VehiclePropertyChangeMode.', '')
243 match = RE_ACCESS.match(line)
244 if match:
245 config.access_modes.append(match.group(1).replace('VehiclePropertyAccess.', ''))
246 match = RE_UNIT.match(line)
247 if match:
248 config.unit_type = match.group(1)
249 match = RE_DATA_ENUM.match(line)
250 if match:
251 config.enum_types.append(match.group(1))
Yu Shan7881e822023-12-15 13:12:01 -0800252 match = RE_VERSION.match(line)
253 if match:
254 if config.version != None:
255 raise Exception('Duplicate version annotation for property: ' + prop_name)
256 config.version = match.group(1)
Yu Shan63e24d72022-06-24 17:53:32 +0000257 else:
258 match = RE_VALUE.match(line)
259 if match:
260 prop_name = match.group(1)
Yu Shan41dd7f12023-06-07 13:25:43 -0700261 if prop_name == 'INVALID':
Yu Shan63e24d72022-06-24 17:53:32 +0000262 continue
Yu Shan8ddd65d2023-06-28 17:02:29 -0700263 if not config.change_mode:
Yu Shan41dd7f12023-06-07 13:25:43 -0700264 raise Exception(
Yu Shan8ddd65d2023-06-28 17:02:29 -0700265 'No change_mode annotation for property: ' + prop_name)
266 if not config.access_modes:
267 raise Exception(
268 'No access_mode annotation for property: ' + prop_name)
Yu Shan7881e822023-12-15 13:12:01 -0800269 if not config.version:
270 raise Exception(
271 'no version annotation for property: ' + prop_name)
Yu Shan8ddd65d2023-06-28 17:02:29 -0700272 config.name = prop_name
273 configs.append(config)
274
275 self.configs = configs
276
277 def convert(self, output, header, footer, cpp, field):
278 """Converts the property config file to C++/Java output file."""
279 counter = 0
280 content = LICENSE + header
281 for config in self.configs:
282 if field == 'change_mode':
283 if cpp:
284 annotation = "VehiclePropertyChangeMode::" + config.change_mode
285 else:
286 annotation = "VehiclePropertyChangeMode." + config.change_mode
287 elif field == 'access_mode':
288 if cpp:
289 annotation = "VehiclePropertyAccess::" + config.access_modes[0]
290 else:
291 annotation = "VehiclePropertyAccess." + config.access_modes[0]
Tyler Trephana1828f92023-10-06 02:39:13 +0000292 elif field == 'enum_types':
293 if len(config.enum_types) < 1:
294 continue;
295 if not cpp:
296 annotation = "List.of(" + ', '.join([class_name + ".class" for class_name in config.enum_types]) + ")"
Yu Shan7881e822023-12-15 13:12:01 -0800297 elif field == 'version':
298 if cpp:
299 annotation = config.version
Yu Shan8ddd65d2023-06-28 17:02:29 -0700300 else:
301 raise Exception('Unknown field: ' + field)
302 if counter != 0:
303 content += '\n'
304 if cpp:
305 content += (TAB + TAB + '{VehicleProperty::' + config.name + ', ' +
306 annotation + '},')
307 else:
308 content += (TAB + TAB + 'Map.entry(VehicleProperty.' + config.name + ', ' +
309 annotation + '),')
310 counter += 1
Yu Shan63e24d72022-06-24 17:53:32 +0000311
Yu Shan41dd7f12023-06-07 13:25:43 -0700312 # Remove the additional ',' at the end for the Java file.
Yu Shan63e24d72022-06-24 17:53:32 +0000313 if not cpp:
314 content = content[:-1]
315
316 content += footer
317
318 with open(output, 'w') as f:
319 f.write(content)
320
Yu Shan8ddd65d2023-06-28 17:02:29 -0700321 def outputAsCsv(self, output):
322 content = 'name,description,change mode,access mode,enum type,unit type\n'
323 for config in self.configs:
324 enum_types = None
325 if not config.enum_types:
326 enum_types = '/'
327 else:
328 enum_types = '/'.join(config.enum_types)
329 unit_type = config.unit_type
330 if not unit_type:
331 unit_type = '/'
332 access_modes = ''
333 content += '"{}","{}","{}","{}","{}","{}"\n'.format(
334 config.name,
335 # Need to escape quote as double quote.
336 config.description.replace('"', '""'),
337 config.change_mode,
338 '/'.join(config.access_modes),
339 enum_types,
340 unit_type)
341
342 with open(output, 'w+') as f:
343 f.write(content)
344
Yu Shan63e24d72022-06-24 17:53:32 +0000345
Yu Shan41dd7f12023-06-07 13:25:43 -0700346def createTempFile():
347 f = tempfile.NamedTemporaryFile(delete=False);
348 f.close();
349 return f.name
350
351
Yu Shan7881e822023-12-15 13:12:01 -0800352class GeneratedFile:
353
354 def __init__(self, type):
355 self.type = type
356 self.cpp_file_path = None
357 self.java_file_path = None
358 self.cpp_header = None
359 self.java_header = None
360 self.cpp_footer = None
361 self.java_footer = None
362 self.cpp_output_file = None
363 self.java_output_file = None
364
365 def setCppFilePath(self, cpp_file_path):
366 self.cpp_file_path = cpp_file_path
367
368 def setJavaFilePath(self, java_file_path):
369 self.java_file_path = java_file_path
370
371 def setCppHeader(self, cpp_header):
372 self.cpp_header = cpp_header
373
374 def setCppFooter(self, cpp_footer):
375 self.cpp_footer = cpp_footer
376
377 def setJavaHeader(self, java_header):
378 self.java_header = java_header
379
380 def setJavaFooter(self, java_footer):
381 self.java_footer = java_footer
382
383 def convert(self, file_parser, check_only, temp_files):
384 if self.cpp_file_path:
385 output_file = GeneratedFile._getOutputFile(self.cpp_file_path, check_only, temp_files)
386 file_parser.convert(output_file, self.cpp_header, self.cpp_footer, True, self.type)
387 self.cpp_output_file = output_file
388
389 if self.java_file_path:
390 output_file = GeneratedFile._getOutputFile(self.java_file_path, check_only, temp_files)
391 file_parser.convert(output_file, self.java_header, self.java_footer, False, self.type)
392 self.java_output_file = output_file
393
394 def cmp(self):
395 if self.cpp_file_path:
396 if not filecmp.cmp(self.cpp_output_file, self.cpp_file_path):
397 return False
398
399 if self.java_file_path:
400 if not filecmp.cmp(self.java_output_file, self.java_file_path):
401 return False
402
403 return True
404
405 @staticmethod
406 def _getOutputFile(file_path, check_only, temp_files):
407 if not check_only:
408 return file_path
409
410 temp_file = createTempFile()
411 temp_files.append(temp_file)
412 return temp_file
413
414
Yu Shan63e24d72022-06-24 17:53:32 +0000415def main():
Yu Shan41dd7f12023-06-07 13:25:43 -0700416 parser = argparse.ArgumentParser(
417 description='Generate Java and C++ enums based on annotations in VehicleProperty.aidl')
418 parser.add_argument('--android_build_top', required=False, help='Path to ANDROID_BUILD_TOP')
Yu Shanb1fc8c92023-08-30 11:51:04 -0700419 parser.add_argument('--preupload_files', nargs='*', required=False, help='modified files')
Yu Shan41dd7f12023-06-07 13:25:43 -0700420 parser.add_argument('--check_only', required=False, action='store_true',
421 help='only check whether the generated files need update')
Yu Shan8ddd65d2023-06-28 17:02:29 -0700422 parser.add_argument('--output_csv', required=False,
423 help='Path to the parsing result in CSV style, useful for doc generation')
Yu Shan41dd7f12023-06-07 13:25:43 -0700424 args = parser.parse_args();
425 android_top = None
426 output_folder = None
427 if args.android_build_top:
428 android_top = args.android_build_top
429 vehiclePropertyUpdated = False
430 for preuload_file in args.preupload_files:
431 if preuload_file.endswith('VehicleProperty.aidl'):
432 vehiclePropertyUpdated = True
433 break
434 if not vehiclePropertyUpdated:
435 return
436 else:
437 android_top = os.environ['ANDROID_BUILD_TOP']
Yu Shan63e24d72022-06-24 17:53:32 +0000438 if not android_top:
Tyler Trephana1828f92023-10-06 02:39:13 +0000439 print('ANDROID_BUILD_TOP is not in environmental variable, please run source and lunch ' +
Yu Shan41dd7f12023-06-07 13:25:43 -0700440 'at the android root')
Yu Shan63e24d72022-06-24 17:53:32 +0000441
442 aidl_file = os.path.join(android_top, PROP_AIDL_FILE_PATH)
Yu Shan8ddd65d2023-06-28 17:02:29 -0700443 f = FileParser();
444 f.parseFile(aidl_file)
445
446 if args.output_csv:
447 f.outputAsCsv(args.output_csv)
448 return
Yu Shan63e24d72022-06-24 17:53:32 +0000449
Yu Shan7881e822023-12-15 13:12:01 -0800450 generated_files = []
451
452 change_mode = GeneratedFile('change_mode')
453 change_mode.setCppFilePath(os.path.join(android_top, CHANGE_MODE_CPP_FILE_PATH))
454 change_mode.setJavaFilePath(os.path.join(android_top, CHANGE_MODE_JAVA_FILE_PATH))
455 change_mode.setCppHeader(CHANGE_MODE_CPP_HEADER)
456 change_mode.setCppFooter(CPP_FOOTER)
457 change_mode.setJavaHeader(CHANGE_MODE_JAVA_HEADER)
458 change_mode.setJavaFooter(JAVA_FOOTER)
459 generated_files.append(change_mode)
460
461 access_mode = GeneratedFile('access_mode')
462 access_mode.setCppFilePath(os.path.join(android_top, ACCESS_CPP_FILE_PATH))
463 access_mode.setJavaFilePath(os.path.join(android_top, ACCESS_JAVA_FILE_PATH))
464 access_mode.setCppHeader(ACCESS_CPP_HEADER)
465 access_mode.setCppFooter(CPP_FOOTER)
466 access_mode.setJavaHeader(ACCESS_JAVA_HEADER)
467 access_mode.setJavaFooter(JAVA_FOOTER)
468 generated_files.append(access_mode)
469
470 enum_types = GeneratedFile('enum_types')
471 enum_types.setJavaFilePath(os.path.join(android_top, ENUM_JAVA_FILE_PATH))
472 enum_types.setJavaHeader(ENUM_JAVA_HEADER)
473 enum_types.setJavaFooter(JAVA_FOOTER)
474 generated_files.append(enum_types)
475
476 version = GeneratedFile('version')
477 version.setCppFilePath(os.path.join(android_top, VERSION_CPP_FILE_PATH))
478 version.setCppHeader(VERSION_CPP_HEADER)
479 version.setCppFooter(CPP_FOOTER)
480 generated_files.append(version)
481
Yu Shan41dd7f12023-06-07 13:25:43 -0700482 temp_files = []
483
Yu Shan41dd7f12023-06-07 13:25:43 -0700484 try:
Yu Shan7881e822023-12-15 13:12:01 -0800485 for generated_file in generated_files:
486 generated_file.convert(f, args.check_only, temp_files)
Yu Shan41dd7f12023-06-07 13:25:43 -0700487
488 if not args.check_only:
489 return
490
Yu Shan7881e822023-12-15 13:12:01 -0800491 for generated_file in generated_files:
492 if not generated_file.cmp():
493 print('The generated enum files for VehicleProperty.aidl requires update, ')
494 print('Run \npython ' + android_top + '/' + SCRIPT_PATH)
495 sys.exit(1)
Yu Shan41dd7f12023-06-07 13:25:43 -0700496 except Exception as e:
497 print('Error parsing VehicleProperty.aidl')
498 print(e)
499 sys.exit(1)
500 finally:
501 for file in temp_files:
502 os.remove(file)
Yu Shan63e24d72022-06-24 17:53:32 +0000503
504
Yu Shan41dd7f12023-06-07 13:25:43 -0700505if __name__ == '__main__':
Yu Shan63e24d72022-06-24 17:53:32 +0000506 main()