# # This waf script is responsible for building the SDK which can be shipped off to users into # tintin/build/sdk/src_wscript is the file which app developers actually run to build their apps # import json import os import waflib from string import Template from tools.fw_elf_obfuscate import obfuscate COPY = "cp ${SRC} ${TGT}" def _generate_sdk_waf(ctx): """ Build a custom version of waf that includes the waf plugins we need :param bld: :return: """ sdk_waftools = [tool.path_from(ctx.path.parent) for tool in ctx.path.ant_glob('waftools/*.py')] shared_waftools = [ "tools/resources/waftools/generate_resource_ball.py", "tools/resources/waftools/generate_pbpack.py", "tools/resources/waftools/generate_resource_id_header.py", "waftools/file_name_c_define.py", "waftools/ldscript.py", "waftools/objcopy.py", "waftools/pebble_sdk_gcc.py", "waftools/pebble_sdk_version.py", "waftools/xcode_pebble.py" ] pebble_waf_tools = [] for tool in sdk_waftools + shared_waftools: path = ctx.path.parent.find_node(tool) if path is None: ctx.fatal("Trying to bundle non existent resource in pb-waf ({})".format(tool)) pebble_waf_tools.append(path) # We cannot run this as a sub-wscript because we use a specific vendor-provided # wscript that provides the --make-waf option and needs to be run in its own clean # environment def _build_waf(task): bld = task.generator.bld cmd_str = ('cd "{}" && python "{}" distclean configure build --make-waf --tools="{}" &&' 'cp waf "{}"'.format(waf_folder.abspath(), task.inputs[0].abspath(), ','.join(x.abspath() for x in task.inputs[1:]), task.outputs[0].abspath())) try: bld.cmd_and_log(cmd_str, quiet=waflib.Context.BOTH) except waflib.Errors.WafError as e: bld.to_log("out: %s" % e.stdout) bld.to_log("err: %s" % e.stderr) raise e waf_folder = ctx.path.find_node('waf') waf_light = waf_folder.find_node('waf-light') ctx(rule=_build_waf, source=[waf_light, ] + pebble_waf_tools, target=waf_folder.get_bld()) def _copy_common_tools(bld, common_folder_node): """ Copy SDK tools into common/waftools and common/tools :param bld: :param common_folder_node: :return: """ for tool in bld.path.ant_glob(['tools/**/*']): bld(rule=COPY, source=tool, target=common_folder_node.make_node(tool.path_from(bld.path))) shared_tools = [ "tools/binutils.py", "tools/bitmapgen.py", "tools/font/__init__.py", "tools/font/fontgen.py", "tools/generate_appinfo.py", "tools/generate_c_byte_array.py", "tools/mkbundle.py", "tools/pbpack.py", "tools/pbpack_meta_data.py", "tools/pebble_image_routines.py", "tools/pebble_sdk_platform.py", "tools/png2pblpng.py", "tools/stm32_crc.py" ] if bld.env.INTERNAL_SDK_BUILD: shared_tools.append("tools/pebble_sdk_platform_internal.py") for tool in shared_tools: bld(rule=COPY, source=bld.path.parent.find_node(tool), target=common_folder_node.make_node(tool)) resource_waftools = [ "tools/resources/__init__.py", "tools/resources/find_resource_filename.py", "tools/resources/resource_map/__init__.py", "tools/resources/resource_map/resource_generator.py", "tools/resources/resource_map/resource_generator_bitmap.py", "tools/resources/resource_map/resource_generator_font.py", "tools/resources/resource_map/resource_generator_js.py", "tools/resources/resource_map/resource_generator_pbi.py", "tools/resources/resource_map/resource_generator_png.py", "tools/resources/resource_map/resource_generator_raw.py", "tools/resources/types/__init__.py", "tools/resources/types/resource_ball.py", "tools/resources/types/resource_declaration.py", "tools/resources/types/resource_definition.py", "tools/resources/types/resource_object.py" ] for tool in resource_waftools: tool_node = bld.path.parent.find_node(tool) bld(rule=COPY, source=tool_node, target=(common_folder_node.make_node('waftools') .make_node(tool_node.path_from(bld.path.parent.find_node('tools'))))) def options(opt): opt.add_option('--sdk_debug_elf', action='store_true', help='Enable building obfuscated ELF files for SDK debugging.') def configure(conf): if conf.options.sdk_debug_elf: conf.env.INCLUDE_SDK_DEBUG_ELF = True def build(bld): bld(rule=COPY, source=bld.path.find_node('sdk_requirements.txt'), target=bld.path.get_bld().make_node('requirements.txt')) bld(rule=COPY, source=bld.path.find_node('sdk_package.json'), target=bld.path.get_bld().make_node('package.json')) bld(rule=COPY, source=bld.path.find_node('use_requirements.json'), target=bld.path.get_bld().make_node('use_requirements.json')) tintin_home = bld.path.parent platform_folder_node = bld.path.get_bld().make_node(bld.env.PLATFORM_NAME) platform_folder_node.parent.mkdir() bld(features='subst', source=bld.path.find_node('Doxyfile-SDK.template'), target=platform_folder_node.make_node('Doxyfile-SDK.auto'), TINTIN_ROOT=tintin_home.abspath(), PLATFORM_PATH=platform_folder_node.path_from(bld.path.parent)) common_folder_node = bld.path.get_bld().make_node('common') common_folder_node.parent.mkdir() for sdk_file in bld.path.ant_glob(['include/*', 'pebble_app.ld.template']): bld(rule=COPY, source=sdk_file, target=common_folder_node.make_node(sdk_file.path_from(bld.path))) if not bld.env.NOJS: js_tooling_path = os.path.dirname(bld.env.JS_TOOLING_SCRIPT.relpath()) for js_tool in ('js_tooling.js', 'generate_snapshot.js'): bld(rule=COPY, source=bld.path.parent.get_bld().make_node(js_tooling_path).make_node(js_tool), target=common_folder_node.make_node('tools').make_node(js_tool), name='copy_rocky_tooling') template_folder_node = common_folder_node.make_node('templates') template_folder_node.parent.mkdir() defaults_node = bld.path.find_node('defaults') # Check whether the default project files are valid templates: with open(defaults_node.find_node('templates.json').abspath()) as f: templates = json.load(f) def _collect_check_templates_tasks(dct): for key in dct: val = dct[key] if isinstance(val, basestring): # avoid unicode, it will trip up waf's Node3 and make it 💩 all over the place val = str(val) template_node = defaults_node.find_node(val.split(os.path.sep)) if not template_node: waflib.Logs.warn( "Could not find {}, but it's defined in " "templates.json".format(val)) continue with open(template_node.abspath()) as tf: try: Template(tf.read()).substitute() except KeyError: pass # This is expected, no args to substitute() except ValueError as e: bld.fatal( "Template error in {}:\n{}\n" "Hint: make sure to escape dollar signs! ($ => $$)".format( template_node.abspath(), e.message)) elif isinstance(val, dict): _collect_check_templates_tasks(val) _collect_check_templates_tasks(templates) # Copy default SDK project files for default_file in bld.path.ant_glob('defaults/**/*'): bld(rule=COPY, source=default_file, target=template_folder_node.make_node(default_file.path_from(defaults_node))) # Generate shims # We shell out to this script because it imports the clang module, which does not run correctly # under pypy. By running python explicitly when calling this script, we avoid the # incompatibility with pypy and clang. native_generator_script = ( bld.path.parent.find_node('tools/generate_native_sdk/generate_pebble_native_sdk_files.py')) export_symbols = bld.path.parent.find_node('tools/generate_native_sdk/exported_symbols.json') source_dir = bld.path.parent.find_node('src') output_source_dir = source_dir.get_bld() with open(export_symbols.abspath()) as f: native_generator_sources = ( [source_dir.find_node(str(header)) for header in json.load(f)['files']]) native_generator_sources.append(export_symbols) native_generator_targets = [bld.path.parent.make_node('src/fw/pebble.auto.c').get_bld(), platform_folder_node.make_node('include/pebble.h'), platform_folder_node.make_node('include/pebble_sdk_version.h'), platform_folder_node.make_node('include/pebble_process_info.h'), platform_folder_node.make_node('include/pebble_worker.h'), platform_folder_node.make_node('include/pebble_worker_sdk_version.h')] bld(rule="cd '{}' ; python '{}' --sdk-dir='{}' '{}' '{}' '{}' '{}' {}". format(tintin_home.abspath(), native_generator_script.abspath(), platform_folder_node.abspath(), export_symbols.abspath(), source_dir.abspath(), output_source_dir.abspath(), bld.env.PLATFORM_NAME, '--internal-sdk-build' if bld.env.INTERNAL_SDK_BUILD else ''), name="generate_native_sdk", source=native_generator_sources, target=native_generator_targets) _generate_sdk_waf(bld) _copy_common_tools(bld, common_folder_node) # Generate our exported font header based on the whitelist in exported_symbols.json. # This is different than our internal header (font_resource_keys.auto.h) as it excludes # some fonts that we don't want to export def _generate_pebble_fonts_h(task): with open(task.outputs[0].abspath(), 'w') as f_out: f_out.write('#pragma once\n') f_out.write('\n') with open(task.inputs[0].abspath(), 'r') as f_in: font_list = json.load(f_in)["fonts"] for font in font_list: f_out.write('#define FONT_KEY_{0} "RESOURCE_ID_{0}"\n'.format(font)) # Copy any font keys over to the SDK bld(rule=_generate_pebble_fonts_h, source=export_symbols, target=platform_folder_node.make_node('include/pebble_fonts.h')) # Generate obfuscated elf file for GDB debugging if bld.env.INCLUDE_SDK_DEBUG_ELF: def _obfuscate_elf(task): input_elf = task.inputs[0].abspath() output_elf = task.outputs[0].abspath() obfuscate(input_elf, output_elf, no_text=False) firmware_build_node = bld.path.parent.get_bld().find_or_declare('src').find_or_declare('fw') bld(rule=_obfuscate_elf, source=firmware_build_node.make_node('tintin_fw.elf'), target=bld.path.get_bld().make_node('{}_sdk_debug.elf'.format(bld.env.PLATFORM_NAME)))