generate_appinfo.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from waflib import Task
  15. import json
  16. import string
  17. import uuid
  18. # This file parses appinfo.json and generates a C file that get's built into
  19. # the pebble app binary.
  20. # See http://wiki.hq.getpebble.com/wiki/index.php/Software/Formats/AppinfoJson
  21. # for details on the format.
  22. def generate_appinfo(input_filename, output_filename):
  23. with open(input_filename, 'r') as json_file:
  24. try:
  25. app_info = json.load(json_file)
  26. except ValueError as e:
  27. raise Exception('Could not parse appinfo.json file: '+ str(e))
  28. generate_appinfo_c(app_info, output_filename)
  29. def generate_appinfo_c(app_info, output_filename, platform_name=None):
  30. # Handle top level options
  31. try:
  32. app_uuid = uuid.UUID(app_info['uuid'])
  33. except KeyError:
  34. raise Exception('Could not find $.uuid in appinfo.json')
  35. uuid_initializer_string = '{ %s }' % ", ".join(["0x%02X" % b for b in app_uuid.bytes])
  36. try:
  37. name = app_info['shortName']
  38. except KeyError:
  39. raise Exception('Could not find $.shortName in appinfo.json')
  40. try:
  41. company_name = app_info['companyName']
  42. except KeyError:
  43. raise Exception('Could not find $.companyName in appinfo.json')
  44. try:
  45. version_label = app_info['versionLabel']
  46. version_label_major = 0
  47. version_label_minor = 0
  48. version_label_list = version_label.split('.')
  49. if len(version_label_list) >= 1:
  50. version_label_major = version_label_list[0]
  51. if len(version_label_list) >= 2:
  52. version_label_minor = version_label_list[1]
  53. if len(version_label_list) > 2:
  54. raise Exception('appinfo.json versionLabel format for app revision must be "Major" or "Major.Minor"')
  55. # validate versionLabel range [0-255] and int-characters
  56. try:
  57. if int(version_label_major) < 0 or int(version_label_major) > 255:
  58. raise ValueError
  59. if int(version_label_minor) < 0 or int(version_label_minor) > 255:
  60. raise ValueError
  61. except ValueError:
  62. raise Exception('appinfo.json versionLabel contains invalid or out of range values [0-255]')
  63. except KeyError:
  64. raise Exception('Could not find $.versionLabel in appinfo.json')
  65. # Handle 'watchapp' options
  66. try:
  67. is_watchface = app_info['watchapp']['watchface']
  68. except KeyError:
  69. is_watchface = False
  70. try:
  71. only_shown_on_communication = app_info['watchapp']['onlyShownOnCommunication']
  72. except KeyError:
  73. only_shown_on_communication = False
  74. try:
  75. is_hidden = app_info['watchapp']['hiddenApp']
  76. except KeyError:
  77. is_hidden = False
  78. # Handle 'resources' options
  79. icon_resource_id = None
  80. try:
  81. for r in app_info['resources']['media']:
  82. if 'menuIcon' in r and r['menuIcon']:
  83. if icon_resource_id is not None:
  84. raise Exception('More than one resource is set to be your menuIcon!')
  85. icon_resource_id = 'RESOURCE_ID_' + r['name']
  86. except KeyError:
  87. pass
  88. if icon_resource_id is None:
  89. icon_resource_id = 'DEFAULT_MENU_ICON'
  90. try:
  91. is_rocky = app_info['projectType'] == 'rocky'
  92. except KeyError:
  93. is_rocky = False
  94. flags = []
  95. if is_watchface:
  96. flags.append('PROCESS_INFO_WATCH_FACE')
  97. if only_shown_on_communication:
  98. flags.append('PROCESS_INFO_VISIBILITY_SHOWN_ON_COMMUNICATION')
  99. if is_hidden:
  100. flags.append('PROCESS_INFO_VISIBILITY_HIDDEN')
  101. if is_rocky:
  102. flags.append('PROCESS_INFO_ROCKY_APP')
  103. if platform_name:
  104. flags.append('PROCESS_INFO_PLATFORM_{}'.format(platform_name.upper()))
  105. if len(flags):
  106. flags_string = ' | '.join(flags)
  107. else:
  108. flags_string = '0'
  109. with open(output_filename, 'w') as f:
  110. f.write('#include "pebble_process_info.h"\n')
  111. f.write('#include "src/resource_ids.auto.h"\n')
  112. f.write(PEBBLE_APP_INFO_TEMPLATE.substitute(
  113. version_major=version_label_major,
  114. version_minor=version_label_minor,
  115. name=name,
  116. company=company_name,
  117. icon_resource_id=icon_resource_id,
  118. flags=flags_string,
  119. uuid=uuid_initializer_string))
  120. PEBBLE_APP_INFO_TEMPLATE = string.Template("""
  121. const PebbleProcessInfo __pbl_app_info __attribute__ ((section (".pbl_header"))) = {
  122. .header = "PBLAPP",
  123. .struct_version = { PROCESS_INFO_CURRENT_STRUCT_VERSION_MAJOR, PROCESS_INFO_CURRENT_STRUCT_VERSION_MINOR },
  124. .sdk_version = { PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR, PROCESS_INFO_CURRENT_SDK_VERSION_MINOR },
  125. .process_version = { ${version_major}, ${version_minor} },
  126. .load_size = 0xb6b6,
  127. .offset = 0xb6b6b6b6,
  128. .crc = 0xb6b6b6b6,
  129. .name = "${name}",
  130. .company = "${company}",
  131. .icon_resource_id = ${icon_resource_id},
  132. .sym_table_addr = 0xA7A7A7A7,
  133. .flags = ${flags},
  134. .num_reloc_entries = 0xdeadcafe,
  135. .uuid = ${uuid},
  136. .virtual_size = 0xb6b6
  137. };
  138. """)