wscript 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #
  2. # This waf script is responsible for building the SDK which can be shipped off to users into
  3. # tintin/build/sdk/src_wscript is the file which app developers actually run to build their apps
  4. #
  5. import json
  6. import os
  7. import waflib
  8. from string import Template
  9. from tools.fw_elf_obfuscate import obfuscate
  10. COPY = "cp ${SRC} ${TGT}"
  11. def _generate_sdk_waf(ctx):
  12. """
  13. Build a custom version of waf that includes the waf plugins we need
  14. :param bld:
  15. :return:
  16. """
  17. sdk_waftools = [tool.path_from(ctx.path.parent) for tool in ctx.path.ant_glob('waftools/*.py')]
  18. shared_waftools = [
  19. "tools/resources/waftools/generate_resource_ball.py",
  20. "tools/resources/waftools/generate_pbpack.py",
  21. "tools/resources/waftools/generate_resource_id_header.py",
  22. "waftools/file_name_c_define.py",
  23. "waftools/ldscript.py",
  24. "waftools/objcopy.py",
  25. "waftools/pebble_sdk_gcc.py",
  26. "waftools/pebble_sdk_version.py",
  27. "waftools/xcode_pebble.py"
  28. ]
  29. pebble_waf_tools = []
  30. for tool in sdk_waftools + shared_waftools:
  31. path = ctx.path.parent.find_node(tool)
  32. if path is None:
  33. ctx.fatal("Trying to bundle non existent resource in pb-waf ({})".format(tool))
  34. pebble_waf_tools.append(path)
  35. # We cannot run this as a sub-wscript because we use a specific vendor-provided
  36. # wscript that provides the --make-waf option and needs to be run in its own clean
  37. # environment
  38. def _build_waf(task):
  39. bld = task.generator.bld
  40. cmd_str = ('cd "{}" && python "{}" distclean configure build --make-waf --tools="{}" &&'
  41. 'cp waf "{}"'.format(waf_folder.abspath(),
  42. task.inputs[0].abspath(),
  43. ','.join(x.abspath() for x in task.inputs[1:]),
  44. task.outputs[0].abspath()))
  45. try:
  46. bld.cmd_and_log(cmd_str, quiet=waflib.Context.BOTH)
  47. except waflib.Errors.WafError as e:
  48. bld.to_log("out: %s" % e.stdout)
  49. bld.to_log("err: %s" % e.stderr)
  50. raise e
  51. waf_folder = ctx.path.find_node('waf')
  52. waf_light = waf_folder.find_node('waf-light')
  53. ctx(rule=_build_waf,
  54. source=[waf_light, ] + pebble_waf_tools,
  55. target=waf_folder.get_bld())
  56. def _copy_common_tools(bld, common_folder_node):
  57. """
  58. Copy SDK tools into common/waftools and common/tools
  59. :param bld:
  60. :param common_folder_node:
  61. :return:
  62. """
  63. for tool in bld.path.ant_glob(['tools/**/*']):
  64. bld(rule=COPY,
  65. source=tool,
  66. target=common_folder_node.make_node(tool.path_from(bld.path)))
  67. shared_tools = [
  68. "tools/binutils.py",
  69. "tools/bitmapgen.py",
  70. "tools/font/__init__.py",
  71. "tools/font/fontgen.py",
  72. "tools/generate_appinfo.py",
  73. "tools/generate_c_byte_array.py",
  74. "tools/mkbundle.py",
  75. "tools/pbpack.py",
  76. "tools/pbpack_meta_data.py",
  77. "tools/pebble_image_routines.py",
  78. "tools/pebble_sdk_platform.py",
  79. "tools/png2pblpng.py",
  80. "tools/stm32_crc.py"
  81. ]
  82. if bld.env.INTERNAL_SDK_BUILD:
  83. shared_tools.append("tools/pebble_sdk_platform_internal.py")
  84. for tool in shared_tools:
  85. bld(rule=COPY,
  86. source=bld.path.parent.find_node(tool),
  87. target=common_folder_node.make_node(tool))
  88. resource_waftools = [
  89. "tools/resources/__init__.py",
  90. "tools/resources/find_resource_filename.py",
  91. "tools/resources/resource_map/__init__.py",
  92. "tools/resources/resource_map/resource_generator.py",
  93. "tools/resources/resource_map/resource_generator_bitmap.py",
  94. "tools/resources/resource_map/resource_generator_font.py",
  95. "tools/resources/resource_map/resource_generator_js.py",
  96. "tools/resources/resource_map/resource_generator_pbi.py",
  97. "tools/resources/resource_map/resource_generator_png.py",
  98. "tools/resources/resource_map/resource_generator_raw.py",
  99. "tools/resources/types/__init__.py",
  100. "tools/resources/types/resource_ball.py",
  101. "tools/resources/types/resource_declaration.py",
  102. "tools/resources/types/resource_definition.py",
  103. "tools/resources/types/resource_object.py"
  104. ]
  105. for tool in resource_waftools:
  106. tool_node = bld.path.parent.find_node(tool)
  107. bld(rule=COPY,
  108. source=tool_node,
  109. target=(common_folder_node.make_node('waftools')
  110. .make_node(tool_node.path_from(bld.path.parent.find_node('tools')))))
  111. def options(opt):
  112. opt.add_option('--sdk_debug_elf', action='store_true',
  113. help='Enable building obfuscated ELF files for SDK debugging.')
  114. def configure(conf):
  115. if conf.options.sdk_debug_elf:
  116. conf.env.INCLUDE_SDK_DEBUG_ELF = True
  117. def build(bld):
  118. bld(rule=COPY,
  119. source=bld.path.find_node('sdk_requirements.txt'),
  120. target=bld.path.get_bld().make_node('requirements.txt'))
  121. bld(rule=COPY,
  122. source=bld.path.find_node('sdk_package.json'),
  123. target=bld.path.get_bld().make_node('package.json'))
  124. bld(rule=COPY,
  125. source=bld.path.find_node('use_requirements.json'),
  126. target=bld.path.get_bld().make_node('use_requirements.json'))
  127. tintin_home = bld.path.parent
  128. platform_folder_node = bld.path.get_bld().make_node(bld.env.PLATFORM_NAME)
  129. platform_folder_node.parent.mkdir()
  130. bld(features='subst',
  131. source=bld.path.find_node('Doxyfile-SDK.template'),
  132. target=platform_folder_node.make_node('Doxyfile-SDK.auto'),
  133. TINTIN_ROOT=tintin_home.abspath(),
  134. PLATFORM_PATH=platform_folder_node.path_from(bld.path.parent))
  135. common_folder_node = bld.path.get_bld().make_node('common')
  136. common_folder_node.parent.mkdir()
  137. for sdk_file in bld.path.ant_glob(['include/*', 'pebble_app.ld.template']):
  138. bld(rule=COPY,
  139. source=sdk_file,
  140. target=common_folder_node.make_node(sdk_file.path_from(bld.path)))
  141. if not bld.env.NOJS:
  142. js_tooling_path = os.path.dirname(bld.env.JS_TOOLING_SCRIPT.relpath())
  143. for js_tool in ('js_tooling.js', 'generate_snapshot.js'):
  144. bld(rule=COPY,
  145. source=bld.path.parent.get_bld().make_node(js_tooling_path).make_node(js_tool),
  146. target=common_folder_node.make_node('tools').make_node(js_tool),
  147. name='copy_rocky_tooling')
  148. template_folder_node = common_folder_node.make_node('templates')
  149. template_folder_node.parent.mkdir()
  150. defaults_node = bld.path.find_node('defaults')
  151. # Check whether the default project files are valid templates:
  152. with open(defaults_node.find_node('templates.json').abspath()) as f:
  153. templates = json.load(f)
  154. def _collect_check_templates_tasks(dct):
  155. for key in dct:
  156. val = dct[key]
  157. if isinstance(val, str):
  158. # avoid unicode, it will trip up waf's Node3 and make it 💩 all over the place
  159. val = str(val)
  160. template_node = defaults_node.find_node(val.split(os.path.sep))
  161. if not template_node:
  162. waflib.Logs.warn(
  163. "Could not find {}, but it's defined in "
  164. "templates.json".format(val))
  165. continue
  166. with open(template_node.abspath()) as tf:
  167. try:
  168. Template(tf.read()).substitute()
  169. except KeyError:
  170. pass # This is expected, no args to substitute()
  171. except ValueError as e:
  172. bld.fatal(
  173. "Template error in {}:\n{}\n"
  174. "Hint: make sure to escape dollar signs! ($ => $$)".format(
  175. template_node.abspath(), e.message))
  176. elif isinstance(val, dict):
  177. _collect_check_templates_tasks(val)
  178. _collect_check_templates_tasks(templates)
  179. # Copy default SDK project files
  180. for default_file in bld.path.ant_glob('defaults/**/*'):
  181. bld(rule=COPY,
  182. source=default_file,
  183. target=template_folder_node.make_node(default_file.path_from(defaults_node)))
  184. # Generate shims
  185. # We shell out to this script because it imports the clang module, which does not run correctly
  186. # under pypy. By running python explicitly when calling this script, we avoid the
  187. # incompatibility with pypy and clang.
  188. native_generator_script = (
  189. bld.path.parent.find_node('tools/generate_native_sdk/generate_pebble_native_sdk_files.py'))
  190. export_symbols = bld.path.parent.find_node('tools/generate_native_sdk/exported_symbols.json')
  191. source_dir = bld.path.parent.find_node('src')
  192. output_source_dir = source_dir.get_bld()
  193. with open(export_symbols.abspath()) as f:
  194. native_generator_sources = (
  195. [source_dir.find_node(str(header)) for header in json.load(f)['files']])
  196. native_generator_sources.append(export_symbols)
  197. native_generator_targets = [bld.path.parent.make_node('src/fw/pebble.auto.c').get_bld(),
  198. platform_folder_node.make_node('include/pebble.h'),
  199. platform_folder_node.make_node('include/pebble_sdk_version.h'),
  200. platform_folder_node.make_node('include/pebble_process_info.h'),
  201. platform_folder_node.make_node('include/pebble_worker.h'),
  202. platform_folder_node.make_node('include/pebble_worker_sdk_version.h')]
  203. bld(rule="cd '{}' ; python '{}' --sdk-dir='{}' '{}' '{}' '{}' '{}' {}".
  204. format(tintin_home.abspath(),
  205. native_generator_script.abspath(),
  206. platform_folder_node.abspath(),
  207. export_symbols.abspath(),
  208. source_dir.abspath(),
  209. output_source_dir.abspath(),
  210. bld.env.PLATFORM_NAME,
  211. '--internal-sdk-build' if bld.env.INTERNAL_SDK_BUILD else ''),
  212. name="generate_native_sdk",
  213. source=native_generator_sources,
  214. target=native_generator_targets)
  215. _generate_sdk_waf(bld)
  216. _copy_common_tools(bld, common_folder_node)
  217. # Generate our exported font header based on the whitelist in exported_symbols.json.
  218. # This is different than our internal header (font_resource_keys.auto.h) as it excludes
  219. # some fonts that we don't want to export
  220. def _generate_pebble_fonts_h(task):
  221. with open(task.outputs[0].abspath(), 'w') as f_out:
  222. f_out.write('#pragma once\n')
  223. f_out.write('\n')
  224. with open(task.inputs[0].abspath(), 'r') as f_in:
  225. font_list = json.load(f_in)["fonts"]
  226. for font in font_list:
  227. f_out.write('#define FONT_KEY_{0} "RESOURCE_ID_{0}"\n'.format(font))
  228. # Copy any font keys over to the SDK
  229. bld(rule=_generate_pebble_fonts_h,
  230. source=export_symbols,
  231. target=platform_folder_node.make_node('include/pebble_fonts.h'))
  232. # Generate obfuscated elf file for GDB debugging
  233. if bld.env.INCLUDE_SDK_DEBUG_ELF:
  234. def _obfuscate_elf(task):
  235. input_elf = task.inputs[0].abspath()
  236. output_elf = task.outputs[0].abspath()
  237. obfuscate(input_elf, output_elf, no_text=False)
  238. firmware_build_node = bld.path.parent.get_bld().find_or_declare('src').find_or_declare('fw')
  239. bld(rule=_obfuscate_elf,
  240. source=firmware_build_node.make_node('tintin_fw.elf'),
  241. target=bld.path.get_bld().make_node('{}_sdk_debug.elf'.format(bld.env.PLATFORM_NAME)))