# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from waflib import Task import json import string import uuid # This file parses appinfo.json and generates a C file that get's built into # the pebble app binary. # See http://wiki.hq.getpebble.com/wiki/index.php/Software/Formats/AppinfoJson # for details on the format. def generate_appinfo(input_filename, output_filename): with open(input_filename, 'r') as json_file: try: app_info = json.load(json_file) except ValueError as e: raise Exception('Could not parse appinfo.json file: '+ str(e)) generate_appinfo_c(app_info, output_filename) def generate_appinfo_c(app_info, output_filename, platform_name=None): # Handle top level options try: app_uuid = uuid.UUID(app_info['uuid']) except KeyError: raise Exception('Could not find $.uuid in appinfo.json') uuid_initializer_string = '{ %s }' % ", ".join(["0x%02X" % ord(b) for b in app_uuid.bytes]) try: name = app_info['shortName'] except KeyError: raise Exception('Could not find $.shortName in appinfo.json') try: company_name = app_info['companyName'] except KeyError: raise Exception('Could not find $.companyName in appinfo.json') try: version_label = app_info['versionLabel'] version_label_major = 0 version_label_minor = 0 version_label_list = version_label.split('.') if len(version_label_list) >= 1: version_label_major = version_label_list[0] if len(version_label_list) >= 2: version_label_minor = version_label_list[1] if len(version_label_list) > 2: raise Exception('appinfo.json versionLabel format for app revision must be "Major" or "Major.Minor"') # validate versionLabel range [0-255] and int-characters try: if int(version_label_major) < 0 or int(version_label_major) > 255: raise ValueError if int(version_label_minor) < 0 or int(version_label_minor) > 255: raise ValueError except ValueError: raise Exception('appinfo.json versionLabel contains invalid or out of range values [0-255]') except KeyError: raise Exception('Could not find $.versionLabel in appinfo.json') # Handle 'watchapp' options try: is_watchface = app_info['watchapp']['watchface'] except KeyError: is_watchface = False try: only_shown_on_communication = app_info['watchapp']['onlyShownOnCommunication'] except KeyError: only_shown_on_communication = False try: is_hidden = app_info['watchapp']['hiddenApp'] except KeyError: is_hidden = False # Handle 'resources' options icon_resource_id = None try: for r in app_info['resources']['media']: if 'menuIcon' in r and r['menuIcon']: if icon_resource_id is not None: raise Exception('More than one resource is set to be your menuIcon!') icon_resource_id = 'RESOURCE_ID_' + r['name'] except KeyError: pass if icon_resource_id is None: icon_resource_id = 'DEFAULT_MENU_ICON' try: is_rocky = app_info['projectType'] == 'rocky' except KeyError: is_rocky = False flags = [] if is_watchface: flags.append('PROCESS_INFO_WATCH_FACE') if only_shown_on_communication: flags.append('PROCESS_INFO_VISIBILITY_SHOWN_ON_COMMUNICATION') if is_hidden: flags.append('PROCESS_INFO_VISIBILITY_HIDDEN') if is_rocky: flags.append('PROCESS_INFO_ROCKY_APP') if platform_name: flags.append('PROCESS_INFO_PLATFORM_{}'.format(platform_name.upper())) if len(flags): flags_string = ' | '.join(flags) else: flags_string = '0' with open(output_filename, 'w') as f: f.write('#include "pebble_process_info.h"\n') f.write('#include "src/resource_ids.auto.h"\n') f.write(PEBBLE_APP_INFO_TEMPLATE.substitute( version_major=version_label_major, version_minor=version_label_minor, name=name, company=company_name, icon_resource_id=icon_resource_id, flags=flags_string, uuid=uuid_initializer_string).encode('utf-8')) PEBBLE_APP_INFO_TEMPLATE = string.Template(""" const PebbleProcessInfo __pbl_app_info __attribute__ ((section (".pbl_header"))) = { .header = "PBLAPP", .struct_version = { PROCESS_INFO_CURRENT_STRUCT_VERSION_MAJOR, PROCESS_INFO_CURRENT_STRUCT_VERSION_MINOR }, .sdk_version = { PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, PROCESS_INFO_CURRENT_SDK_VERSION_MINOR }, .process_version = { ${version_major}, ${version_minor} }, .load_size = 0xb6b6, .offset = 0xb6b6b6b6, .crc = 0xb6b6b6b6, .name = "${name}", .company = "${company}", .icon_resource_id = ${icon_resource_id}, .sym_table_addr = 0xA7A7A7A7, .flags = ${flags}, .num_reloc_entries = 0xdeadcafe, .uuid = ${uuid}, .virtual_size = 0xb6b6 }; """)