pebble/tools/generate_appinfo.py
2025-01-27 11:38:16 -08:00

162 lines
5.5 KiB
Python

# 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
};
""")