#!/usr/bin/env python '''Generate simConst.py from simConst.h distributed from CoppeliaSim $ ./generate_simConst.py > ../pyrep/backend/simConst.py ''' import contextlib import importlib import os import os.path as osp import re import shlex import shutil import subprocess import sys import tempfile try: import CppHeaderParser except ImportError: print('Please run following:\n\tpip install CppHeaderParser', file=sys.stderr) sys.exit(1) def get_coppeliasim_root(): if 'COPPELIASIM_ROOT' not in os.environ: raise RuntimeError('Please set env COPPELIASIM_ROOT') return os.environ['COPPELIASIM_ROOT'] def import_as(filename, module): spec = importlib.util.spec_from_file_location( module, filename ) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def generate_simConst_py(): header_file = osp.join( get_coppeliasim_root(), 'programming/include/simConst.h' ) with contextlib.redirect_stdout(sys.stderr): header = CppHeaderParser.CppHeader(header_file) assert header.classes == {} assert header.functions == [] assert header.global_enums == {} names = [] for define in header.defines: m = re.match('^(.*)/\*.*\*/', define) if m: define = m.groups()[0] splits = define.split() if len(splits) == 2: name, _ = splits if name in ['SIM_PROGRAM_VERSION']: continue names.append(name) for enum in header.enums: assert enum['namespace'] == '' for value in enum['values']: names.append(value['name']) names.append('') out_dir = tempfile.mkdtemp() cpp_file = osp.join(out_dir, 'generate_simConst_py.cpp') with open(cpp_file, 'w') as f: f.write('#include \n') f.write('#include \n') f.write('int main() {\n') for name in names: if name: f.write( f'\tstd::cout << "{name} = " << {name} << std::endl;\n' ) else: f.write('\tstd::cout << std::endl;\n') f.write('}\n') out_file = osp.join(out_dir, 'generate_simConst_py') cmd = f'g++ {cpp_file} -o {out_file} -I{osp.dirname(header_file)}' subprocess.check_call(shlex.split(cmd)) cmd = osp.join(out_dir, 'generate_simConst_py') code = subprocess.check_output(cmd).strip().decode() shutil.rmtree(out_dir) return code def merge_simConst_py(code): official_file = osp.join( get_coppeliasim_root(), 'programming/remoteApiBindings/python/python/simConst.py') official = import_as(official_file, 'official') out_dir = tempfile.mkdtemp() generated_file = osp.join(out_dir, 'simConst_generated.py') with open(generated_file, 'w') as f: f.write(code) generated = import_as(generated_file, 'generated') name_and_value = [] for name in dir(official): if re.match('__.*__', name): continue if name in dir(generated): value_official = getattr(official, name) value_generated = getattr(generated, name) if value_official != value_generated: print(f"WARNING: The values of var '{name}' is not equal: " f'official={value_official}, ' f'generated={value_generated}', file=sys.stderr) else: name_and_value.append((name, getattr(official, name))) code = ('# This file is automatically generated by ' f'{osp.basename(__file__)} from simConst.h\n\n' + code) code += ('\n\n# Followings are copied from official simConst.py ' '(so possibly deprecated in C++ API)') for name, value in name_and_value: code += f'\n{name} = {value}' code += '\n' return code def main(): code = generate_simConst_py() code = merge_simConst_py(code) print(code, end='') if __name__ == '__main__': main()