1import os 2import sys 3import argparse 4import shutil 5import tempfile 6import json 7import subprocess 8 9base_path = os.path.abspath(os.path.dirname(__file__)) 10sys.path.insert(0, base_path) 11 12project_path = os.path.abspath(os.path.join(base_path, '..', '..')) 13docs_path = os.path.join(project_path, 'docs') 14sys.path.insert(0, docs_path) 15 16import create_fake_lib_c # NOQA 17import pycparser_monkeypatch # NOQA 18import pycparser # NOQA 19 20DEVELOP = False 21 22 23temp_directory = tempfile.mkdtemp(suffix='.lvgl_json') 24 25 26def run(output_path, lvgl_config_path, output_to_stdout, target_header, filter_private, no_docstrings, *compiler_args): 27 28 pycparser_monkeypatch.FILTER_PRIVATE = filter_private 29 30 lvgl_path = project_path 31 lvgl_src_path = os.path.join(lvgl_path, 'src') 32 temp_lvgl = os.path.join(temp_directory, 'lvgl') 33 target_header_base_name = ( 34 os.path.splitext(os.path.split(target_header)[-1])[0] 35 ) 36 37 try: 38 os.mkdir(temp_lvgl) 39 shutil.copytree(lvgl_src_path, os.path.join(temp_lvgl, 'src')) 40 shutil.copyfile(os.path.join(lvgl_path, 'lvgl.h'), os.path.join(temp_lvgl, 'lvgl.h')) 41 42 pp_file = os.path.join(temp_directory, target_header_base_name + '.pp') 43 44 if lvgl_config_path is None: 45 lvgl_config_path = os.path.join(lvgl_path, 'lv_conf_template.h') 46 47 with open(lvgl_config_path, 'rb') as f: 48 data = f.read().decode('utf-8').split('\n') 49 50 for i, line in enumerate(data): 51 if line.startswith('#if 0'): 52 data[i] = '#if 1' 53 else: 54 for item in ( 55 'LV_USE_LOG', 56 'LV_USE_OBJ_ID', 57 'LV_USE_OBJ_ID_BUILTIN', 58 'LV_USE_FLOAT', 59 'LV_USE_BIDI', 60 'LV_USE_LODEPNG', 61 'LV_USE_LIBPNG', 62 'LV_USE_BMP', 63 'LV_USE_TJPGD', 64 'LV_USE_LIBJPEG_TURBO', 65 'LV_USE_GIF', 66 'LV_BIN_DECODER_RAM_LOAD', 67 'LV_USE_RLE', 68 'LV_USE_QRCODE', 69 'LV_USE_BARCODE', 70 'LV_USE_TINY_TTF', 71 'LV_USE_GRIDNAV', 72 'LV_USE_FRAGMENT', 73 'LV_USE_IMGFONT', 74 'LV_USE_SNAPSHOT', 75 'LV_USE_FREETYPE' 76 ): 77 if line.startswith(f'#define {item} '): 78 data[i] = f'#define {item} 1' 79 break 80 81 with open(os.path.join(temp_directory, 'lv_conf.h'), 'wb') as f: 82 f.write('\n'.join(data).encode('utf-8')) 83 else: 84 src = lvgl_config_path 85 dst = os.path.join(temp_directory, 'lv_conf.h') 86 shutil.copyfile(src, dst) 87 88 include_dirs = [temp_directory, project_path] 89 90 if sys.platform.startswith('win'): 91 import get_sdl2 92 93 try: 94 import pyMSVC # NOQA 95 except ImportError: 96 sys.stderr.write( 97 '\nThe pyMSVC library is missing, ' 98 'please run "pip install pyMSVC" to install it.\n' 99 ) 100 sys.stderr.flush() 101 sys.exit(-500) 102 103 env = pyMSVC.setup_environment() # NOQA 104 cpp_cmd = ['cl', '/std:c11', '/nologo', '/P'] 105 output_pp = f'/Fi"{pp_file}"' 106 sdl2_include, _ = get_sdl2.get_sdl2(temp_directory) 107 include_dirs.append(sdl2_include) 108 include_path_env_key = 'INCLUDE' 109 110 elif sys.platform.startswith('darwin'): 111 include_path_env_key = 'C_INCLUDE_PATH' 112 cpp_cmd = [ 113 'clang', '-std=c11', '-E', '-DINT32_MIN=0x80000000', 114 ] 115 output_pp = f' >> "{pp_file}"' 116 else: 117 include_path_env_key = 'C_INCLUDE_PATH' 118 cpp_cmd = [ 119 'gcc', '-std=c11', '-E', '-Wno-incompatible-pointer-types', 120 ] 121 output_pp = f' >> "{pp_file}"' 122 123 fake_libc_path = create_fake_lib_c.run(temp_directory) 124 125 if include_path_env_key not in os.environ: 126 os.environ[include_path_env_key] = '' 127 128 os.environ[include_path_env_key] = ( 129 f'{fake_libc_path}{os.pathsep}{os.environ[include_path_env_key]}' 130 ) 131 132 if 'PATH' not in os.environ: 133 os.environ['PATH'] = '' 134 135 os.environ['PATH'] = ( 136 f'{fake_libc_path}{os.pathsep}{os.environ["PATH"]}' 137 ) 138 139 cpp_cmd.extend(compiler_args) 140 cpp_cmd.extend([ 141 '-DLV_LVGL_H_INCLUDE_SIMPLE', 142 '-DLV_CONF_INCLUDE_SIMPLE', 143 '-DLV_USE_DEV_VERSION' 144 ]) 145 146 cpp_cmd.extend(['-DPYCPARSER', f'"-I{fake_libc_path}"']) 147 cpp_cmd.extend([f'"-I{item}"' for item in include_dirs]) 148 cpp_cmd.append(f'"{target_header}"') 149 150 if sys.platform.startswith('win'): 151 cpp_cmd.insert(len(cpp_cmd) - 2, output_pp) 152 else: 153 cpp_cmd.append(output_pp) 154 155 cpp_cmd = ' '.join(cpp_cmd) 156 157 p = subprocess.Popen( 158 cpp_cmd, 159 stdout=subprocess.PIPE, 160 stderr=subprocess.PIPE, 161 env=os.environ, 162 shell=True 163 ) 164 out, err = p.communicate() 165 exit_code = p.returncode 166 167 if not os.path.exists(pp_file): 168 sys.stdout.write(out.decode('utf-8').strip() + '\n') 169 sys.stdout.write('EXIT CODE: ' + str(exit_code) + '\n') 170 sys.stderr.write(err.decode('utf-8').strip() + '\n') 171 sys.stdout.flush() 172 sys.stderr.flush() 173 174 raise RuntimeError('Unknown Failure') 175 176 with open(pp_file, 'r') as f: 177 pp_data = f.read() 178 179 cparser = pycparser.CParser() 180 ast = cparser.parse(pp_data, target_header) 181 182 ast.setup_docs(no_docstrings, temp_directory) 183 184 if not output_to_stdout and output_path is None: 185 if not DEVELOP: 186 shutil.rmtree(temp_directory) 187 188 return ast 189 190 elif output_to_stdout: 191 # stdout.reset() 192 print(json.dumps(ast.to_dict(), indent=4)) 193 else: 194 if not os.path.exists(output_path): 195 os.makedirs(output_path) 196 197 output_path = os.path.join(output_path, target_header_base_name + '.json') 198 199 with open(output_path, 'w') as f: 200 f.write(json.dumps(ast.to_dict(), indent=4)) 201 202 except Exception as err: 203 try: 204 print(cpp_cmd) # NOQA 205 print() 206 except: # NOQA 207 pass 208 209 for key, value in os.environ.items(): 210 print(key + ':', value) 211 212 print() 213 import traceback 214 215 traceback.print_exc() 216 print() 217 218 exceptions = [ 219 ArithmeticError, 220 AssertionError, 221 AttributeError, 222 EOFError, 223 FloatingPointError, 224 GeneratorExit, 225 ImportError, 226 IndentationError, 227 IndexError, 228 KeyError, 229 KeyboardInterrupt, 230 LookupError, 231 MemoryError, 232 NameError, 233 NotImplementedError, 234 OverflowError, 235 ReferenceError, 236 RuntimeError, 237 StopIteration, 238 SyntaxError, 239 TabError, 240 SystemExit, 241 TypeError, 242 UnboundLocalError, 243 UnicodeError, 244 UnicodeEncodeError, 245 UnicodeDecodeError, 246 UnicodeTranslateError, 247 ValueError, 248 ZeroDivisionError, 249 SystemError 250 ] 251 252 if isinstance(err, OSError): 253 error = err.errno 254 else: 255 if type(err) in exceptions: 256 error = ~exceptions.index(type(err)) 257 else: 258 error = -100 259 else: 260 error = 0 261 262 if DEVELOP: 263 print('temporary file path:', temp_directory) 264 else: 265 shutil.rmtree(temp_directory) 266 267 sys.exit(error) 268 269 270if __name__ == '__main__': 271 parser = argparse.ArgumentParser('-') 272 parser.add_argument( 273 '--output-path', 274 dest='output_path', 275 help=( 276 'output directory for JSON file. If one is not ' 277 'supplied then it will be output stdout' 278 ), 279 action='store', 280 default=None 281 ) 282 parser.add_argument( 283 '--lvgl-config', 284 dest='lv_conf', 285 help=( 286 'path to lv_conf.h (including file name), if this is not set then ' 287 'a config file will be generated that has everything turned on.' 288 ), 289 action='store', 290 default=None 291 ) 292 parser.add_argument( 293 '--develop', 294 dest='develop', 295 help='this option leaves the temporary folder in place.', 296 action='store_true', 297 ) 298 parser.add_argument( 299 "--target-header", 300 dest="target_header", 301 help=( 302 "path to a custom header file. When using this to supply a custom" 303 "header file you MUST insure that any LVGL includes are done so " 304 "they are relitive to the LVGL repository root folder.\n\n" 305 '#include "src/lvgl_private.h"\n\n' 306 "If you have includes to header files that are not LVGL then you " 307 "will need to add the include locations for those header files " 308 "when running this script. It is done using the same manner that " 309 "is used when calling a C compiler\n\n" 310 "You need to provide the absolute path to the header file when " 311 "using this feature." 312 ), 313 action="store", 314 default=os.path.join(temp_directory, "lvgl", "lvgl.h") 315 ) 316 parser.add_argument( 317 '--filter-private', 318 dest='filter_private', 319 help='Internal Use', 320 action='store_true', 321 ) 322 parser.add_argument( 323 '--no-docstrings', 324 dest='no_docstrings', 325 help='Internal Use', 326 action='store_true', 327 ) 328 329 args, extra_args = parser.parse_known_args() 330 331 DEVELOP = args.develop 332 333 run(args.output_path, args.lv_conf, args.output_path is None, args.target_header, args.filter_private, args.no_docstrings, *extra_args) 334