12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730 |
- import json
- import os
- import subprocess
- import sys
- import pexpect
- import zipfile
- import datetime
- import time
- import waflib
- from waflib import Node, Logs
- from waflib.Build import BuildContext
- waf_dir = sys.path[0]
- sys.path.append(os.path.join(waf_dir, 'tools'))
- sys.path.append(os.path.join(waf_dir, 'tools/log_hashing'))
- sys.path.append(os.path.join(waf_dir, 'sdk/tools/'))
- sys.path.append(os.path.join(waf_dir, 'waftools'))
- import waftools.asm
- import waftools.gitinfo
- import waftools.ldscript
- import waftools.openocd
- import waftools.xcode_pebble
- LOGHASH_OUT_PATH = 'src/fw/loghash_dict.json'
- def truncate(msg):
- if msg is None:
- return msg
- # Don't truncate exceptions thrown by waf itself
- if "Traceback " in msg:
- return msg
- truncate_length = 600
- if len(msg) > truncate_length:
- msg = msg[:truncate_length-4] + '...\n' + waflib.Logs.colors.NORMAL
- return msg
- def run_arm_gdb(ctx, elf_node, cmd_str="", target_server_port=3333):
- from tools.gdb_driver import find_gdb_path
- arm_none_eabi_path = find_gdb_path()
- if arm_none_eabi_path is None:
- ctx.fatal("pebble-gdb not found!")
- os.system('{} {} {} --ex="target remote :{}"'.format(
- arm_none_eabi_path, elf_node.path_from(ctx.path),
- cmd_str, target_server_port)
- )
- def options(opt):
- opt.load('pebble_arm_gcc', tooldir='waftools')
- opt.load('show_configure', tooldir='waftools')
- opt.recurse('applib-targets')
- opt.recurse('tests')
- opt.recurse('src/bluetooth-fw')
- opt.recurse('src/fw')
- opt.recurse('src/idl')
- opt.recurse('sdk')
- opt.add_option('--board', action='store',
- choices=[ 'bb2',
- 'ev2_4',
- 'v1_5',
- 'v2_0',
- 'snowy_bb2', # alias for snowy_dvt, but with #define IS_BIGBOARD
- 'snowy_evt2',
- 'snowy_dvt',
- 'snowy_s3',
- 'spalding_bb2', # snowy_bb2 with s4 display
- 'spalding_evt',
- 'spalding',
- 'silk_evt',
- 'silk_bb',
- 'silk',
- 'silk_bb2',
- 'cutts_bb',
- 'robert_bb',
- 'robert_bb2',
- 'robert_evt',
- 'robert_es',
- 'asterix',],
- help='Which board we are targeting '
- 'bb2, snowy_dvt, spalding, silk...')
- opt.add_option('--jtag', action='store', default=None, dest='jtag', # default is bb2 (below)
- choices=waftools.openocd.JTAG_OPTIONS.keys(),
- help='Which JTAG programmer we are using '
- '(bb2 (default), olimex, ev2, etc)')
- opt.add_option('--internal_sdk_build', action='store_true',
- help='Build the internal version of the SDK')
- opt.add_option('--future_ux', action='store_true',
- help='Build future UX features and APIs. Implies --internal_sdk_build.')
- opt.add_option('--nosleep', action='store_true',
- help='Disable sleep and stop mode (to use JTAG+GDB)')
- opt.add_option('--nostop', action='store_true',
- help='Disable stop mode (to use JTAG+GDB)')
- opt.add_option('--lowpowerdebug', action='store_true',
- help='Lowpowerdebug can be toggled from the CLI but is off by default. This just turns it on by default')
- opt.add_option('--nowatch', action='store_true',
- help='Disable the watchface idle timeout')
- opt.add_option('--nowatchdog', action='store_true',
- help='Disable automatic reboots when watchdog fires')
- opt.add_option('--test_apps', action='store_true',
- help='Enables test apps (off by default)')
- opt.add_option('--test_apps_list', type=str,
- help='Specify AppInstallId\'s of the test apps to be compiled with the firmware')
- opt.add_option('--performance_tests', action='store_true',
- help='Enables instrumentation + apps for performance testing (off by default)')
- opt.add_option('--verbose_logs', action='store_true',
- help='Enables verbose logs (off by default)')
- opt.add_option('--ui_debug', action='store_true',
- help='Enable window dump & layer nudge CLI cmd (off by default)')
- opt.add_option('--qemu', action='store_true',
- help='Build an image for qemu instead of a real board.')
- opt.add_option('--nojs', action='store_true', help='Removes js support from the current build.')
- opt.add_option('--sdkshell', action='store_true',
- help='Use the sdk shell instead of the normal shell')
- opt.add_option('--nolog', action='store_true',
- help='Disable PBL_LOG macros to save space')
- opt.add_option('--nohash', action='store_true',
- help='Disable log hashing and make the logs human readable')
- opt.add_option('--log-level', default='debug', choices=['error', 'warn', 'info', 'debug', 'debug_verbose'],
- help='Default global log level')
- opt.add_option('--lang',
- action='store',
- default='en_US',
- help='Which language to package (isocode)')
- opt.add_option('--compile_commands', action='store_true', help='Create a clang compile_commands.json')
- opt.add_option('--file', action='store', help='Specify a file to use with the flash_fw command')
- opt.add_option('--tty',
- help='Selects a tty to use for serial imaging. Must be specified for all image commands')
- opt.add_option('--baudrate', action='store', type=int, help='Optional: specifies the baudrate to run the targetted uart at')
- opt.add_option('--onlysdk', action='store_true', help="only build the sdk")
- opt.add_option('--qemu_host', default='localhost:12345',
- help='host:port for the emulator console connection')
- opt.add_option('--force-fit-tintin', action='store_true',
- help='Force fit for Tintin')
- opt.add_option('--no-link', action='store_true',
- help='Do not link the final firmware binary. This is used for static analysis')
- opt.add_option('--noprompt', action='store_true',
- help='Disable the serial console to save space')
- opt.add_option('--build_test_apps', action='store_true',
- help='Turns on building of test apps')
- opt.add_option('--bb_large_spi', action='store_true',
- help='Sets a flag to use all 8MB of BigBoard flash')
- opt.add_option('--profiler', action='store_true', help='Enable the profiler.')
- opt.add_option('--profile_interrupts', action='store_true',
- help='Enable profiling of all interrupts.')
- opt.add_option('--voice_debug', action='store_true',
- help='Enable all voice logging.')
- opt.add_option('--voice_codec_tests', action='store_true',
- help='Enable voice codec tests. Enables the profiler')
- opt.add_option('--battery_debug', action='store_true',
- help='Set the PMIC\'s max charging voltage to 4.3V.')
- opt.add_option('--no_sandbox', action='store_true',
- help='Disable the MPU for 3rd party apps.')
- opt.add_option('--malloc_instrumentation', action='store_true',
- help='Enables malloc instrumentation')
- opt.add_option('--infinite_backlight', action='store_true',
- help='Makes the backlight never time-out.')
- opt.add_option('--mfg', action='store_true', help='Enable specific MFG-only options in the PRF build')
- opt.add_option('--no-pulse-everywhere',
- action='store_true',
- help='Disables PULSE everywhere, uses legacy logs and prompt')
- opt.add_option('--bootloader-test', action='store', default='none',
- choices=['none', 'stage1', 'stage2'],
- help='Build bootloader test (stage1 or stage2). Implies --mfg.')
- opt.add_option('--reboot_on_bt_crash', action='store_true', help='Forces a BT '
- 'chip crash to immediately force a system reboot instead of just cycling airplane mode. '
- 'This makes it easier for us to actually get crash info')
- def handle_configure_options(conf):
- if conf.options.noprompt:
- conf.env.append_value('DEFINES', 'DISABLE_PROMPT')
- conf.env.DISABLE_PROMPT = True
- if conf.options.beta or conf.options.release:
- conf.env.append_value('DEFINES', 'RELEASE')
- if conf.options.malloc_instrumentation:
- conf.env.append_value('DEFINES', 'MALLOC_INSTRUMENTATION')
- print("Enabling malloc instrumentation")
- if conf.options.qemu:
- conf.env.append_value('DEFINES', 'TARGET_QEMU')
- if conf.options.test_apps_list:
- conf.options.test_apps = True
- conf.env.test_apps_list = conf.options.test_apps_list.split(",")
- print("Enabling test apps: " + str(conf.options.test_apps_list))
- if conf.options.build_test_apps or conf.options.test_apps:
- conf.env.BUILD_TEST_APPS = True
- if conf.options.performance_tests:
- conf.env.PERFORMANCE_TESTS = True
- if conf.options.voice_debug:
- conf.env.VOICE_DEBUG = True
- if conf.options.voice_codec_tests:
- conf.env.VOICE_CODEC_TESTS = True
- conf.env.append_value('DEFINES', 'VOICE_CODEC_TESTS')
- conf.options.profiler = True
- if conf.env.MICRO_FAMILY == 'STM32F4':
- if conf.options.lowpowerdebug and not conf.options.nosleep:
- Logs.warn('On snowy --lowpowerdebug can only be used with --nosleep. Forcing --nosleep on!\n'
- 'See PBL-10174.')
- conf.env.append_value('DEFINES', 'PBL_NOSLEEP')
- if 'bb' in conf.options.board:
- conf.env.append_value('DEFINES', 'IS_BIGBOARD')
- if conf.options.nosleep:
- conf.env.append_value('DEFINES', 'PBL_NOSLEEP')
- print("Sleep/stop mode disabled")
- if conf.options.nostop:
- conf.env.append_value('DEFINES', 'PBL_NOSTOP')
- print("Stop mode disabled")
- if conf.options.lowpowerdebug:
- conf.env.append_value('DEFINES', 'LOW_POWER_DEBUG')
- print("Sleep and Stop mode debugging enabled")
- if conf.options.nowatch:
- conf.env.append_value('DEFINES', 'NO_WATCH_TIMEOUT')
- print("Watch watchdog disabled")
- if conf.options.nowatchdog:
- conf.env.append_value('DEFINES', 'NO_WATCHDOG')
- conf.env.NO_WATCHDOG = True
- print("Watchdog reboot disabled")
- if conf.options.reboot_on_bt_crash:
- conf.env.append_value('DEFINES', 'REBOOT_ON_BT_CRASH=1')
- print("BT now crash will trigger an MCU reboot")
- if conf.options.test_apps:
- conf.env.append_value('DEFINES', 'ENABLE_TEST_APPS')
- print("Im in ur firmware, bloatin ur binz! (Test apps enabled)")
- if conf.options.performance_tests:
- conf.env.append_value('DEFINES', 'PERFORMANCE_TESTS')
- conf.options.profiler = True
- print("Instrumentation and apps for performance measurement enabled (enables profiler)")
- if conf.options.verbose_logs:
- conf.env.append_value('DEFINES', 'VERBOSE_LOGGING')
- print("Verbose logging enabled")
- print(f"Log level: {conf.options.log_level.upper()}")
- conf.env.append_value('DEFINES', f'DEFAULT_LOG_LEVEL=LOG_LEVEL_{conf.options.log_level.upper()}')
- if conf.options.ui_debug:
- conf.env.append_value('DEFINES', 'UI_DEBUG')
- if conf.options.no_sandbox or conf.options.qemu:
- print("Sandbox disabled")
- else:
- conf.env.append_value('DEFINES', 'APP_SANDBOX')
- if conf.options.bb_large_spi:
- conf.env.append_value('DEFINES', 'LARGE_SPI_FLASH')
- print("Enabling 8MB BigBoard flash")
- if not conf.options.nolog:
- conf.env.append_value('DEFINES', 'PBL_LOG_ENABLED')
- if not conf.options.nohash:
- conf.env.append_value('DEFINES', 'PBL_LOGS_HASHED')
- if conf.options.profile_interrupts:
- conf.env.append_value('DEFINES', 'PROFILE_INTERRUPTS')
- if not conf.options.profiler:
- # Can't profile interrupts without the profiler enabled
- print("Enabling profiler")
- conf.options.profiler = True
- if conf.options.profiler:
- conf.env.append_value('DEFINES', 'PROFILER')
- if not conf.options.nostop:
- print("Enable --nostop for accurate profiling.")
- conf.env.append_value('DEFINES', 'PBL_NOSTOP')
- if conf.options.voice_debug:
- conf.env.append_value('DEFINES', 'VOICE_DEBUG')
- if conf.options.battery_debug:
- conf.env.append_value('DEFINES', 'BATTERY_DEBUG')
- print("Enabling higher battery charge voltage.")
- if conf.options.future_ux and not conf.is_tintin():
- print("Future UX features enabled.")
- conf.env.FUTURE_UX = True
- conf.env.INTERNAL_SDK_BUILD = bool(conf.options.internal_sdk_build)
- if conf.env.INTERNAL_SDK_BUILD:
- print("Internal SDK enabled")
- if conf.options.force_fit_tintin:
- conf.env.append_value('DEFINES', 'TINTIN_FORCE_FIT')
- print("Functionality is secondary to usability")
- if (conf.is_snowy_compatible() and not conf.options.no_lto) or conf.options.lto:
- conf.options.lto = True
- print("Turning on LTO.")
- if conf.options.no_link:
- conf.env.NO_LINK = True
- print("Not linking firmware")
- if conf.options.infinite_backlight and 'bb' in conf.options.board:
- conf.env.append_value('DEFINES', 'INFINITE_BACKLIGHT')
- print("Enabling infinite backlight.")
- if conf.options.bootloader_test in ['stage1', 'stage2']:
- print("Forcing MFG on for bootloader test build.")
- conf.options.mfg = True
- if conf.options.bootloader_test == 'stage1':
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE1=1')
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE2=0')
- elif conf.options.bootloader_test == 'stage2':
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE1=0')
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE2=1')
- else:
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE1=0')
- conf.env.append_value('DEFINES', 'BOOTLOADER_TEST_STAGE2=0')
- if not conf.options.no_pulse_everywhere:
- conf.env.append_value('DEFINES', 'PULSE_EVERYWHERE=1')
- def _create_cm0_env(conf):
- prev_env = conf.env
- prev_variant = conf.variant
- # Create a new Cortex M0 environment that's used to build for the DA14681:
- conf.setenv('cortex-m0')
- # Copy the defines fron the stock env into our m0 env
- conf.env.append_unique('DEFINES', prev_env.DEFINES)
- Logs.pprint('CYAN', 'Configuring ARM cortex-m0 environment')
- conf.env.append_unique('DEFINES', 'ARCH_NO_NATIVE_LONG_DIVIDE')
- CPU_FLAGS = ['-mcpu=cortex-m0', '-mthumb']
- OPT_FLAGS = [
- '-fvar-tracking-assignments', # Track variable locations better
- '-fmessage-length=0', '-fsigned-char',
- '-fbuiltin',
- '-fno-builtin-itoa',
- '-ffreestanding',
- '-Os',
- ]
- if not conf.options.no_debug:
- OPT_FLAGS += [
- '-g3',
- '-gdwarf-4', # More detailed debug info
- ]
- C_FLAGS = ['-std=c11', '-ffunction-sections',
- '-Wall', '-Wextra', '-Werror', '-Wpointer-arith',
- '-Wno-unused-parameter', '-Wno-missing-field-initializers',
- '-Wno-error=unused-parameter',
- '-Wno-error=unused-const-variable',
- '-Wno-packed-bitfield-compat',
- '-Wno-address-of-packed-member',
- '-Wno-expansion-to-defined',
- '-Wno-enum-int-mismatch',
- '-Wno-enum-conversion']
- conf.find_program('arm-none-eabi-gcc', var='CC', mandatory=True)
- conf.env.AS = conf.env.CC
- for tool in ['ar', 'objcopy']:
- conf.find_program('arm-none-eabi-' + tool, var=tool.upper(),
- mandatory=True)
- conf.env.append_unique('CFLAGS', CPU_FLAGS + OPT_FLAGS + C_FLAGS)
- ASFLAGS = ['-x', 'assembler-with-cpp']
- conf.env.append_unique('ASFLAGS', ASFLAGS + CPU_FLAGS + OPT_FLAGS)
- conf.env.append_unique('LINKFLAGS',
- ['-Wl,--cref',
- '-Wl,--gc-sections',
- '-nostdlib',
- ] + CPU_FLAGS + OPT_FLAGS)
- conf.load('gcc gas objcopy ldscript')
- conf.load('file_name_c_define')
- conf.variant = prev_variant
- conf.env = prev_env
- def configure(conf):
- if not conf.options.board:
- conf.fatal('No board selected! '
- 'You must pass a --board argument when configuring.')
- # Has to be 'waftools.gettext' as unadorned 'gettext' will find the gettext
- # module in the standard library.
- conf.load('waftools.gettext')
- conf.recurse('platform')
- conf.env.QEMU = conf.options.qemu
- conf.env.NOJS = conf.options.nojs
- # The BT controller is the only thing different between robert_es and robert_evt, so just
- # retend robert_es is robert_evt. We'll be removing robert_es fairly soon anyways.
- bt_board = None
- if conf.options.board == 'robert_es':
- bt_board = 'robert_es'
- conf.options.board = 'robert_evt'
- if conf.options.jtag:
- conf.env.JTAG = conf.options.jtag
- elif conf.options.board in ('snowy_bb2', 'spalding_bb2'):
- conf.env.JTAG = 'jtag_ftdi'
- elif conf.options.board in ('cutts_bb', 'robert_bb', 'robert_bb2', 'robert_evt',
- 'silk_evt', 'silk_bb', 'silk_bb2', 'silk'):
- conf.env.JTAG = 'swd_ftdi'
- elif conf.options.board in ('asterix'):
- conf.env.JTAG = 'swd_cmsisdap'
- else:
- # default to bb2
- conf.env.JTAG = 'bb2'
- # Cutts and Robert access flash through the ITCM bus (except in QEMU)
- if (conf.is_cutts() or conf.is_robert()) and not conf.env.QEMU:
- conf.env.FLASH_ITCM = True
- else:
- conf.env.FLASH_ITCM = False
- # Set platform used for building the SDK
- if conf.is_tintin():
- conf.env.PLATFORM_NAME = 'aplite'
- conf.env.MIN_SDK_VERSION = 2
- elif conf.is_spalding():
- conf.env.PLATFORM_NAME = 'chalk'
- conf.env.MIN_SDK_VERSION = 3
- elif conf.is_snowy_compatible():
- conf.env.PLATFORM_NAME = 'basalt'
- conf.env.MIN_SDK_VERSION = 2
- elif conf.is_silk() or conf.is_asterix():
- conf.env.PLATFORM_NAME = 'diorite'
- conf.env.MIN_SDK_VERSION = 2
- elif conf.is_cutts() or conf.is_robert():
- conf.env.PLATFORM_NAME = 'emery'
- conf.env.MIN_SDK_VERSION = 3
- else:
- conf.fatal('No platform specified for {}!'.format(conf.options.board))
- # Save this for later
- conf.env.BOARD = conf.options.board
- if conf.is_tintin():
- conf.env.MICRO_FAMILY = 'STM32F2'
- elif conf.is_snowy_compatible() or conf.is_silk():
- conf.env.MICRO_FAMILY = 'STM32F4'
- elif conf.is_cutts() or conf.is_robert():
- conf.env.MICRO_FAMILY = 'STM32F7'
- elif conf.is_asterix():
- conf.env.MICRO_FAMILY = 'NRF52840'
- else:
- conf.fatal('No micro family specified for {}!'.format(conf.options.board))
- if conf.options.mfg:
- # Note that for the most part PRF and MFG firmwares are the same, so for MFG PRF builds
- # both MANUFACTURING_FW and RECOVERY_FW will be defined.
- conf.env.IS_MFG = True
- conf.env.append_value('DEFINES', ['MANUFACTURING_FW'])
- conf.find_program('node nodejs', var='NODE',
- errmsg="Unable to locate the Node command. "
- "Please check your Node installation and try again.")
- conf.recurse('third_party')
- conf.recurse('src/idl')
- conf.recurse('src/fw')
- conf.recurse('sdk')
- conf.recurse('bin/boot')
- waftools.openocd.write_cfg(conf)
- # Save a baseline environment that we'll use for unit tests
- # Detach so operations against conf.env don't affect unit_test_env
- unit_test_env = conf.env.derive()
- unit_test_env.detach()
- # Save a baseline environment that we'll use for ARM environments
- base_env = conf.env
- handle_configure_options(conf)
- # robert_es is the exact same as robert_evt, except for the BT chip, so gets converted to
- # robert_evt above, but we need to handle it as robert_es here.
- if bt_board is None:
- bt_board = conf.get_board()
- # Select BT controller based on configuration:
- if conf.env.QEMU:
- conf.env.bt_controller = 'qemu'
- conf.env.append_value('DEFINES', ['BT_CONTROLLER_QEMU'])
- elif conf.is_tintin() or conf.is_snowy() or conf.is_spalding():
- conf.env.bt_controller = 'cc2564x'
- conf.env.append_value('DEFINES', ['BT_CONTROLLER_CC2564X'])
- elif conf.is_asterix():
- conf.env.bt_controller = 'nrf52'
- conf.env.append_value('DEFINES', ['BT_CONTROLLER_NRF52'])
- elif bt_board in ('silk_bb2', 'silk', 'robert_bb2', 'robert_evt'):
- conf.env.bt_controller = 'da14681-01'
- conf.env.append_value('DEFINES', ['BT_CONTROLLER_DA14681'])
- else:
- conf.env.bt_controller = 'da14681-00'
- conf.env.append_value('DEFINES', ['BT_CONTROLLER_DA14681'])
- _create_cm0_env(conf)
- conf.recurse('src/bluetooth-fw')
- Logs.pprint('CYAN', 'Configuring arm_firmware environment')
- conf.setenv('', base_env)
- conf.load('pebble_arm_gcc', tooldir='waftools')
- conf.setenv('arm_prf_mode', env=conf.env)
- conf.env.append_value('DEFINES', ['RECOVERY_FW'])
- Logs.pprint('CYAN', 'Configuring unit test environment')
- conf.setenv('local', unit_test_env)
- # if sys.platform.startswith('linux'):
- # libclang_path = subprocess.check_output(['llvm-config', '--libdir']).strip()
- # conf.env.append_value('INCLUDES', [os.path.join(libclang_path, 'clang/3.2/include/'),])
- # The waf clang tool likes to use llvm-ar as it's ar tool, but that doesn't work on our build
- # servers. Fall back to boring old ar. This will populate the 'AR' env variable so future
- # searches for what value to put into env['AR'] will find this one.
- conf.find_program('ar')
- conf.load('clang')
- conf.load('pebble_test', tooldir='waftools')
- conf.env.CLAR_DIR = conf.path.make_node('tools/clar/').abspath()
- conf.env.CFLAGS = [ '-std=c11',
- '-Wall',
- '-Werror',
- '-Wno-error=unused-variable',
- '-Wno-error=unused-function',
- '-Wno-error=missing-braces',
- '-Wno-error=unused-const-variable',
- '-Wno-error=address-of-packed-member',
- '-Wno-enum-conversion',
- '-g3',
- '-gdwarf-4',
- '-O0',
- '-fdata-sections',
- '-ffunction-sections' ]
- conf.env.append_value('DEFINES', 'CLAR_FIXTURE_PATH="' +
- conf.path.make_node('tests/fixtures/').abspath() + '"')
- conf.env.append_value('DEFINES', 'PBL_LOG_ENABLED')
- if conf.options.compile_commands:
- conf.load('clang_compilation_database', tooldir='waftools')
- if not os.path.lexists('compile_commands.json'):
- filename = 'compile_commands.json'
- source = conf.path.get_bld().make_node(filename)
- os.symlink(source.path_from(conf.path), filename)
- prev_env = conf.env
- Logs.pprint('CYAN', 'Configuring 32 bit host environment')
- # Copy 'local' to serve as the basis for '32bit':
- env_32bit = conf.env.derive().detach()
- env_32bit.append_value('CFLAGS', '-m32')
- env_32bit.append_value('LINKFLAGS', '-m32')
- env_32bit.LINK_CC = 'gcc'
- conf.all_envs['32bit'] = env_32bit
- conf.set_env(prev_env)
- # Note: this will modify the 'local' conf when targeting emscripten:
- conf.recurse('applib-targets')
- Logs.pprint('CYAN', 'Configuring stored apps environment')
- conf.setenv('stored_apps', base_env)
- conf.recurse('stored_apps')
- # Confirm that requirements-*.txt and requirements-osx-brew.txt have been satisfied.
- import tool_check
- tool_check.tool_check()
- # Warn user not to use Cutts BB build with a Robert screen
- if conf.options.board == 'cutts_bb':
- Logs.warn('NOTE: Do not use this build with a C2/Robert display '
- '(6V6 rail will damage the display)')
- def _run_remote_suite(ctx, suite):
- # PEBBLESDK_TEST_ROOT must be defined in order to initiate integration tests
- try:
- pebblesdk_test_root = os.environ['PEBBLESDK_TEST_ROOT']
- except KeyError:
- waflib.Logs.pprint('RED', 'Error: environment variable $PEBBLESDK_TEST_ROOT must be defined')
- return
- # Check if firmware has been built
- # Assume we're looking for a "normal" PBZ, as recovery PBZs aren't supported by integration tests
- fw_bin_path = ctx.get_tintin_fw_node().abspath()
- fw_bin_exists = os.path.isfile(fw_bin_path)
- if not fw_bin_exists:
- waflib.Logs.pprint('RED', ('Error: BIN not found at expected location {}, '
- 'have you run `waf build` yet?'.format(fw_bin_path)))
- return
- # Check if firmware has been bundled
- version_string, version_ts, _ = _get_version_info(ctx)
- fw_type = 'qemu' if ctx.env.QEMU else 'normal'
- fw_pbz_path = ctx.get_pbz_node(fw_type, ctx.env.BOARD, version_string).abspath()
- fw_pbz_exists = os.path.isfile(fw_pbz_path)
- if not fw_pbz_exists:
- waflib.Logs.pprint('CYAN', ('Warning: PBZ not found at expected location {}, '
- 'running `waf bundle`...').format(fw_pbz_path))
- bundle(ctx)
- # Run power tests using remote_runner.py
- remote_runner_path = os.path.join(pebblesdk_test_root, 'remote_runner.py')
- if not os.path.isfile(remote_runner_path):
- waflib.Logs.pprint('RED', ('Error: remote_runner.py not found in {}. '
- 'Are you sure that PEBBLESDK_TEST_ROOT is defined correctly?'
- .format(pebblesdk_test_root)))
- return
- subprocess.call([remote_runner_path, '--pbz', fw_pbz_path, '[%s]' % suite])
- class power_test(BuildContext):
- cmd = 'power_test'
- def execute_build(ctx):
- _run_remote_suite(ctx, 'power')
- class integration_test(BuildContext):
- cmd = 'integration_test'
- def execute_build(ctx):
- _run_remote_suite(ctx, 'tintin_3x')
- def stop_build_timer(ctx):
- t = datetime.datetime.utcnow() - ctx.pbl_build_start_time
- node = ctx.path.get_bld().make_node('build_time')
- with open(node.abspath(), 'w') as fout:
- fout.write(str(int(round(t.total_seconds()))))
- def build(bld):
- bld.DYNAMIC_RESOURCES = []
- bld.LOGHASH_DICTS = []
- # Start this timer here to include the time to generate tasks.
- bld.pbl_build_start_time = datetime.datetime.utcnow()
- bld.add_post_fun(stop_build_timer)
- if bld.variant in ('test', 'test_rocky_emx', 'applib'):
- bld.set_env(bld.all_envs['local'])
- bld.load('file_name_c_define', tooldir='waftools')
- bld.recurse('platform')
- bld.recurse('src/idl')
- if bld.cmd == 'install':
- raise Exception("install isn't a supported command. Did you mean flash?")
- if bld.variant == 'pdc2png':
- bld.recurse('src/libutil')
- bld.recurse('tools')
- return
- if bld.variant == 'tools':
- bld.recurse('tools')
- return
- if bld.variant in ('', 'applib', 'prf'):
- # Dependency for SDK
- bld.recurse('src/fw/vendor/jerryscript')
- if bld.variant == '':
- # sdk generation
- bld.recurse('sdk')
- if bld.variant == 'applib':
- bld.recurse('resources')
- bld.recurse('src/libutil')
- bld.recurse('src/fw')
- bld.recurse('third_party/nanopb')
- bld.recurse('src/include')
- bld.recurse('applib-targets')
- return
- if bld.options.onlysdk:
- # required to build src/idl
- bld.recurse('third_party/nanopb')
- # stop here, sdk generation is done
- return
- # Do not enable stationary mode in PRF or release firmware
- if (bld.variant != 'prf' and not bld.env.QEMU and bld.env.NORMAL_SHELL != 'sdk'):
- bld.env.append_value('DEFINES', 'STATIONARY_MODE')
- if bld.variant == 'prf':
- bld.set_env(bld.all_envs['arm_prf_mode'])
- elif bld.variant == 'test':
- if bld.env.APPLIB_TARGET == 'emscripten':
- bld.fatal('Did you mean ./waf test_rocky_emx ?')
- bld.recurse('src/include')
- bld.recurse('src/fw/vendor/jerryscript')
- bld.recurse('third_party/nanopb')
- bld.recurse('src/libbtutil')
- bld.recurse('src/libos')
- bld.recurse('src/libutil')
- bld.recurse('tests')
- bld.recurse('tools')
- return
- elif bld.variant == 'test_rocky_emx':
- if bld.env.APPLIB_TARGET != 'emscripten':
- bld.fatal('Make sure to ./waf configure with --target=emscripten')
- bld.recurse('src/libutil')
- bld.recurse('src/libos')
- bld.recurse('src/fw/vendor/jerryscript')
- bld.recurse('third_party/nanopb')
- bld.recurse('applib-targets')
- bld.recurse('tools')
- bld.recurse('tests')
- return
- if bld.variant == '':
- bld.recurse('stored_apps')
- bld.recurse('third_party')
- bld.recurse('src/include')
- bld.recurse('src/libbtutil')
- bld.recurse('src/bluetooth-fw')
- bld.recurse('src/libc')
- bld.recurse('src/libos')
- bld.recurse('src/libutil')
- bld.recurse('src/fw')
- if sys.platform != 'darwin':
- bld.recurse('tools/qemu_spi_cooker')
- # Generate resources. Leave this until the end so we collect all the env['DYNAMIC_RESOURCES']
- # values that the other build steps added.
- bld.recurse('resources')
- # if we're not linking the firmware don't run these
- if not bld.env.NO_LINK:
- bld.add_post_fun(size_fw)
- bld.add_post_fun(size_resources)
- if 'PBL_LOGS_HASHED' in bld.env.DEFINES:
- bld.add_post_fun(merge_loghash_dicts)
- class build_prf(BuildContext):
- """executes the recovery firmware build"""
- cmd = 'build_prf'
- variant = 'prf'
- class build_applib(BuildContext):
- cmd = 'build_applib'
- variant = 'applib'
- def merge_loghash_dicts(bld):
- loghash_dict = bld.path.get_bld().make_node(LOGHASH_OUT_PATH)
- import log_hashing.newlogging
- log_hashing.newlogging.merge_loghash_dict_json_files(loghash_dict, bld.LOGHASH_DICTS)
- class SizeFirmware(BuildContext):
- cmd = 'size_fw'
- fun = 'size_fw'
- def size_fw(ctx):
- """prints size information of the firmware"""
- fw_elf = ctx.get_tintin_fw_node().change_ext('.elf')
- if fw_elf is None:
- ctx.fatal('No fw ELF found for size')
- fw_bin = ctx.get_tintin_fw_node()
- if fw_bin is None:
- ctx.fatal('No fw BIN found for size')
- import binutils
- text, data, bss = binutils.size(fw_elf.abspath())
- total = text + data
- output = ('{:>7} {:>7} {:>7} {:>7} {:>7} filename\n'
- '{:7} {:7} {:7} {:7} {:7x} tintin_fw.elf'.
- format('text', 'data', 'bss', 'dec', 'hex', text, data, bss, total, total))
- Logs.pprint('YELLOW', '\n' + output)
- try:
- space_left = _check_firmware_image_size(ctx, fw_bin.path_from(ctx.path))
- except FirmwareTooLargeException as e:
- if ctx.env.MICRO_FAMILY == 'STM32F2' and ctx.env.QEMU:
- # Let us off with a warning for now
- Logs.warn(str(e))
- else:
- ctx.fatal(str(e))
- else:
- Logs.pprint('CYAN', 'FW: ' + space_left)
- class SizeResources(BuildContext):
- cmd = 'size_resources'
- fun = 'size_resources'
- def size_resources(ctx):
- """prints size information of resources"""
- if ctx.variant == 'prf':
- return
- pbpack_path = ctx.path.get_bld().find_node('system_resources.pbpack')
- if pbpack_path is None:
- ctx.fatal('No resource pbpack found')
- if ctx.env.MICRO_FAMILY == 'STM32F4':
- max_size = 512 * 1024
- elif ctx.env.MICRO_FAMILY == 'STM32F7':
- max_size = 1024 * 1024
- elif ctx.env.MICRO_FAMILY == 'NRF52840':
- max_size = 1024 * 1024
- else:
- max_size = 256 * 1024
- pbpack_actual_size = os.path.getsize(pbpack_path.path_from(ctx.path))
- bytes_free = max_size - pbpack_actual_size
- from waflib import Logs
- Logs.pprint('CYAN', 'Resources: %d/%d (%d free)\n' % (pbpack_actual_size, max_size, bytes_free))
- if pbpack_actual_size > max_size:
- ctx.fatal('Resources are too large for target board %d > %d'
- % (pbpack_actual_size, max_size))
- def size(ctx):
- from waflib import Options
- Options.commands = ['size_fw', 'size_resources'] + Options.commands
- class size_prf(BuildContext):
- """checks the size of PRF"""
- cmd = 'size_prf'
- variant = 'prf'
- class test(BuildContext):
- """builds and runs the tests"""
- cmd = 'test'
- variant = 'test'
- class test_rocky_emx(BuildContext):
- """builds and runs the tests"""
- cmd = 'test_rocky_emx'
- variant = 'test_rocky_emx'
- def docs(ctx):
- """builds the documentation out to build/doxygen"""
- ctx.exec_command('doxygen Doxyfile', stdout=None, stderr=None)
- class DocsSdk(BuildContext):
- """builds the sdk documentation out to build/sdk/<platformname>/doxygen_sdk"""
- cmd = 'docs_sdk'
- fun = 'docs_sdk'
- def docs_sdk(ctx):
- pebble_sdk = ctx.path.get_bld().make_node('sdk')
- supported_platforms = pebble_sdk.listdir()
- for platform in supported_platforms:
- doxyfile = pebble_sdk.find_node(platform).find_node('Doxyfile-SDK.auto')
- if doxyfile:
- ctx.exec_command('doxygen {}'.format(doxyfile.path_from(ctx.path)),
- stdout=None, stderr=None)
- def docs_all(ctx):
- """builds the documentation with all dependency graphs out to build/doxygen"""
- ctx.exec_command('doxygen Doxyfile-all-graphs', stdout=None, stderr=None)
- # Bundle commands
- #################################################
- def _get_version_info(ctx):
- # FIXME: it's probably a better idea to lift board + version info from the .bin file... this can get out of sync!
- git_revision = waftools.gitinfo.get_git_revision(ctx)
- if git_revision['TAG'] != '?':
- version_string = git_revision['TAG']
- version_ts = int(git_revision['TIMESTAMP'])
- version_commit = git_revision['COMMIT']
- else:
- version_string = 'dev'
- version_ts = 0
- version_commit = ''
- return version_string, version_ts, version_commit
- def _make_bundle(ctx, fw_bin_path, fw_type='normal', board=None, resource_path=None, write=True):
- import mkbundle
- if board is None:
- board = ctx.env.BOARD
- b = mkbundle.PebbleBundle()
- version_string, version_ts, version_commit = _get_version_info(ctx)
- out_file = ctx.get_pbz_node(fw_type, ctx.env.BOARD, version_string).path_from(ctx.path)
- try:
- _check_firmware_image_size(ctx, fw_bin_path)
- b.add_firmware(fw_bin_path, fw_type, version_ts, version_commit, board, version_string)
- except FirmwareTooLargeException as e:
- ctx.fatal(str(e))
- except mkbundle.MissingFileException as e:
- ctx.fatal('Error: Missing file ' + e.filename + ', have you run ./waf build yet?')
- if resource_path is not None:
- b.add_resources(resource_path, version_ts)
- if 'RELEASE' not in ctx.env.DEFINES and 'PBL_LOGS_HASHED' in ctx.env.DEFINES:
- loghash_dict = ctx.path.get_bld().make_node(LOGHASH_OUT_PATH).abspath()
- b.add_loghash(loghash_dict)
- # Add a LICENSE.txt file
- b.add_license('LICENSE')
- # make sure ctx.capability is available
- ctx.recurse('platform', mandatory=False)
- if ctx.capability('HAS_JAVASCRIPT'):
- js_tooling = ctx.path.get_bld().find_node('src/fw/vendor/jerryscript/js_tooling/js_tooling.js')
- if js_tooling is not None:
- b.add_jstooling(js_tooling.path_from(ctx.path), ctx.capability('JAVASCRIPT_BYTECODE_VERSION'))
- if fw_type == 'normal':
- layouts_node = ctx.path.get_bld().find_node('resources/layouts.json.auto')
- if layouts_node is not None:
- b.add_layouts(layouts_node.path_from(ctx.path))
- if write:
- b.write(out_file)
- waflib.Logs.pprint('CYAN', 'Writing bundle to: %s' % out_file)
- return b
- class BundleCommand(BuildContext):
- cmd = 'bundle'
- fun = 'bundle'
- def bundle(ctx):
- """bundles a firmware"""
- if ctx.env.QEMU:
- bundle_qemu(ctx)
- else:
- _make_bundle(ctx, ctx.get_tintin_fw_node().path_from(ctx.path),
- resource_path=ctx.get_pbpack_node().path_from(ctx.path))
- class bundle_prf(BuildContext):
- """bundles a recovery firmware"""
- cmd = 'bundle_prf'
- variant = 'prf'
- def execute_build(ctx):
- _make_bundle(ctx, ctx.get_tintin_fw_node().path_from(ctx.path), fw_type='recovery')
- def _bundle_resourceless_fw(ctx, fw_path, fw_type):
- # We need to create a dummy pbpack and bundle it in. Some FW images don't use
- # resources, but the firmware will refuse to upgrade to a firmware if a resource
- # file isn't sent over, regardless of it's validity.
- import tempfile
- # We need to actually write some content in here or else the phone app won't think the
- # resources are valid, and put_bytes will refuse to update to any firmware image if it doesn't
- # come with a corresponding resource pack. No one will ever read these though so who cares
- # what the content is.
- with tempfile.NamedTemporaryFile(delete=False) as dummy_pbpack:
- dummy_pbpack.write('DUMMY')
- pbpack_path = dummy_pbpack.name
- try:
- _make_bundle(ctx, fw_path, fw_type=fw_type, resource_path=pbpack_path)
- finally:
- os.remove(pbpack_path)
- class bundle_recovery(BuildContext):
- """bundles a recovery firmware as normal firmware"""
- cmd = 'bundle_recovery'
- variant = 'prf'
- def execute_build(ctx):
- _bundle_resourceless_fw(ctx, ctx.get_tintin_fw_node().path_from(ctx.path),
- fw_type='recovery')
- class BundleQEMUCommand(BuildContext):
- cmd = 'bundle_qemu'
- fun = 'bundle_qemu'
- def bundle_qemu(ctx):
- """bundle QEMU images together into a "fake" PBZ"""
- qemu_image_micro(ctx)
- qemu_image_spi(ctx)
- b = _make_bundle(ctx, ctx.get_tintin_fw_node().path_from(ctx.path),
- resource_path=ctx.get_pbpack_node().path_from(ctx.path),
- write=False, board='qemu_{}'.format(ctx.env.BOARD))
- version_string, _, _ = _get_version_info(ctx)
- qemu_pbz = ctx.get_pbz_node('qemu', ctx.env.BOARD, version_string)
- out_file = qemu_pbz.path_from(ctx.path)
- with zipfile.ZipFile(out_file, 'w', compression=zipfile.ZIP_DEFLATED) as pbz_file:
- pbz_file.writestr('manifest.json', json.dumps(b.bundle_manifest))
- files = [ctx.get_tintin_fw_node(),
- ctx.get_pbpack_node(),
- 'qemu_micro_flash.bin',
- 'qemu_spi_flash.bin']
- if 'PBL_LOGS_HASHED' in ctx.env.DEFINES:
- files.append(LOGHASH_OUT_PATH)
- for fitem in files:
- if isinstance(fitem, Node.Node):
- fnode = fitem
- else:
- fnode = ctx.path.get_bld().make_node(fitem)
- img_path = fnode.path_from(ctx.path)
- pbz_file.write(img_path, os.path.basename(img_path))
- waflib.Logs.pprint('CYAN', 'Writing bundle to: %s' % out_file)
- class QemuImageMicroCommand(BuildContext):
- cmd = 'qemu_image_micro'
- fun = 'qemu_image_micro'
- class QemuImageMicroPrfCommand(BuildContext):
- cmd = 'qemu_image_prf_micro'
- fun = 'qemu_image_prf_micro'
- class QemuImageSpiCommand(BuildContext):
- cmd = 'qemu_image_spi'
- fun = 'qemu_image_spi'
- class MfgImageSpiCommand(BuildContext):
- cmd = 'mfg_image_spi'
- fun = 'mfg_image_spi'
- def qemu_image_micro(ctx):
- fw_hex = ctx.get_tintin_fw_node().change_ext('.hex')
- _create_qemu_image_micro(ctx, fw_hex.path_from(ctx.path))
- def qemu_image_prf_micro(ctx):
- fw_hex = ctx.get_tintin_fw_node_prf().change_ext('.hex')
- _create_qemu_image_micro(ctx, fw_hex.path_from(ctx.path))
- def _create_qemu_image_micro(ctx, path_to_firmware_hex):
- """creates the micro-flash image for qemu"""
- from intelhex import IntelHex
- if not ctx.env.BOOTLOADER_HEX:
- ctx.fatal('Board "{}" does not have a bootloader binary available'
- .format(ctx.env.BOARD))
- micro_flash_node = ctx.path.get_bld().make_node('qemu_micro_flash.bin')
- micro_flash_path = micro_flash_node.path_from(ctx.path)
- waflib.Logs.pprint('CYAN', 'Writing micro flash image to {}'.format(micro_flash_path))
- img = IntelHex(ctx.env.BOOTLOADER_HEX)
- img.merge(IntelHex(path_to_firmware_hex), overlap='replace')
- # Write firwmare image and pad up to next 512 byte multiple. This is because QEMU
- # assumes all block devices are multiples of 512 byte sectors
- img.padding = 0xff
- flash_end = ((img.maxaddr() + 511) // 512) * 512
- img.tobinfile(micro_flash_path, start=0x08000000, end=flash_end-1)
- def _create_spi_flash_image(ctx, name):
- spi_flash_node = ctx.path.get_bld().make_node(name)
- spi_flash_path = spi_flash_node.path_from(ctx.path)
- waflib.Logs.pprint('CYAN', 'Writing SPI flash image to {}'.format(spi_flash_path))
- return spi_flash_path
- def qemu_image_spi(ctx):
- """creates a SPI flash image for qemu"""
- if ctx.env.BOARD.startswith('silk'):
- resources_begin = 0x100000
- image_size = 0x800000
- elif ctx.env.BOARD.startswith('robert') or ctx.env.BOARD.startswith('cutts'):
- resources_begin = 0x200000
- image_size = 0x1000000
- elif ctx.env.MICRO_FAMILY == 'STM32F4':
- resources_begin = 0x380000
- image_size = 0x1000000
- else:
- resources_begin = 0x280000
- image_size = 0x400000
- spi_flash_path = _create_spi_flash_image(ctx, 'qemu_spi_flash.bin')
- with open(spi_flash_path, 'wb') as qemu_spi_img_file:
- # Pad the first section before system resources with FF's'
- qemu_spi_img_file.write(bytes([0xff]) * resources_begin)
- # Write system resources:
- pbpack = ctx.get_pbpack_node()
- res_img = open(pbpack.path_from(ctx.path), 'rb').read()
- qemu_spi_img_file.write(res_img)
- # Pad with 0xFF up to image size
- tail_padding_size = image_size - resources_begin - len(res_img)
- qemu_spi_img_file.write(bytes([0xff]) * tail_padding_size)
- # qemu_spi_cooker is broken on OSX but it doesn't really matter
- # it's only there to speed up first boot, an empty image will do
- if sys.platform != 'darwin':
- with open(os.devnull, 'w') as null:
- qemu_spi_cooker_node = ctx.path.get_bld().make_node('qemu_spi_cooker')
- qemu_spi_cooker_path = qemu_spi_cooker_node.path_from(ctx.path)
- subprocess.check_call([qemu_spi_cooker_path, spi_flash_path], stdout=null)
- def mfg_image_spi(ctx):
- """Creates a SPI flash image of PRF for MFG pre-burn. Includes a
- FirmwareDescription struct"""
- import insert_firmware_descr
- if ctx.env.BOARD.startswith('silk'):
- prf_begin = 0x200000
- image_size = 0x800000
- else:
- ctx.fatal("MFG Image not suppored for board: {}".format(ctx.env.BOARD))
- spi_flash_path = _create_spi_flash_image(ctx, 'mfg_prf_image.bin')
- mfg_spi_img_file = open(spi_flash_path, 'wb')
- # Pad the first section before PRF storage
- mfg_spi_img_file.write("\xff" * prf_begin)
- prf_path = ctx.get_tintin_fw_node_prf().path_from(ctx.path)
- prf_image = insert_firmware_descr.insert_firmware_description_struct(prf_path)
- mfg_spi_img_file.write(prf_image)
- # Pad with 0xff up to image size
- tail_padding_size = image_size - prf_begin - len(prf_image)
- mfg_spi_img_file.write("\xff" * tail_padding_size)
- def show_ttys(ctx):
- """Displays all available ftdi ports connected to computer"""
- os.system("python ./tools/log_hashing/miniterm_co.py ftdi:///?")
- class ConsoleCommand(BuildContext):
- cmd = 'console'
- fun = 'console'
- def console(ctx):
- """Starts miniterm with the serial console."""
- # miniterm is not made to be used as a python module, so just shell out:
- tty = ctx.options.tty or _get_dbgserial_tty()
- if _is_pulse_everywhere(ctx):
- os.system("python ./tools/pulse_console.py -t %s" % tty)
- else:
- baudrate = ctx.options.baudrate or 230400
- os.system("python ./tools/log_hashing/miniterm_co.py %s %d" % (tty, baudrate))
- class ConsoleCommand(BuildContext):
- cmd = 'console_prf'
- fun = 'console_prf'
- def console_prf(ctx):
- os.putenv("PBL_CONSOLE_DICT_PATH", "build/prf/src/fw/loghash_dict.json")
- console(ctx)
- class BleConsoleCommand(BuildContext):
- cmd = 'ble_console'
- fun = 'ble_console'
- def ble_console(ctx):
- def _get_ble_tty():
- import pebble_tty
- tty = pebble_tty.find_ble_tty()
- if tty is None:
- return None
- waflib.Logs.pprint('GREEN', 'No --tty argument specified, auto-selecting: %s' % tty)
- return tty
- """Starts miniterm with the serial console for the BLE chip."""
- ctx.recurse('platform', mandatory=False)
- # FIXME: We have the ability to progam PIDs into the new round of Big Boards. TTY
- # path discovery should be able to use that (PBL-31111). For now, just make a best
- # guess at what the path should be
- if ctx.is_silk() or ctx.is_robert():
- tty_path = _get_ble_tty()
- # if the bt_controller was chosen explicitly, assume we are using an eval board, which
- # happens to match the path for cutts
- elif ctx.uses_dialog_bluetooth():
- tty_path = "ftdi://ftdi:2232:1/1"
- else:
- waflib.Logs.pprint('CYAN', 'Note: This platform does not have a BLE UART')
- tty_path = _get_dbgserial_tty()
- tty = ctx.options.tty or tty_path
- baudrate = ctx.options.baudrate or 230400
- os.system("python ./tools/log_hashing/miniterm_co.py %s %d" % (tty, baudrate))
- class BleConsolePrfCommand(BuildContext):
- cmd = 'ble_console_prf'
- fun = 'ble_console_prf'
- def ble_console_prf(ctx):
- os.putenv("PBL_CONSOLE_DICT_PATH", "build/prf/src/fw/loghash_dict.json")
- ble_console(ctx)
- def accessory_console(ctx):
- def _get_accessory_tty():
- import pebble_tty
- tty = pebble_tty.find_accessory_tty()
- if tty is None:
- return None
- waflib.Logs.pprint('GREEN', 'No --tty argument specified, auto-selecting: %s' % tty)
- return tty
- """Starts miniterm with the accessory connector console."""
- # miniterm is not made to be used as a python module, so just shell out:
- tty = ctx.options.tty or _get_accessory_tty()
- baudrate = ctx.options.baudrate or 115200
- os.system("python ./tools/log_hashing/miniterm_co.py %s %d" % (tty, baudrate))
- def qemu(ctx):
- # Make sure the micro-flash image is up to date. By default, we don't rebuild the
- # SPI flash image in case you want to continue with the stored apps, etc. you had before.
- from waflib import Options
- Options.commands = ['qemu_image_micro', 'qemu_launch'] + Options.commands
- def qemu_prf(ctx):
- # Make sure the micro-flash image is up to date. By default, we don't rebuild the
- # SPI flash image in case you want to continue with the stored apps, etc. you had before.
- from waflib import Options
- Options.commands = ['qemu_image_prf_micro', 'qemu_launch'] + Options.commands
- class QemuLaunchCommand(BuildContext):
- cmd = 'qemu_launch'
- fun = 'qemu_launch'
- def qemu_launch(ctx):
- """Starts up the emulator (qemu) """
- ctx.recurse('platform', mandatory=False)
- qemu_machine = ctx.get_qemu_machine()
- if not qemu_machine or qemu_machine == 'unknown':
- raise Exception("Board type '{}' not supported by QEMU".format(ctx.env.BOARD))
- qemu_micro_flash = ctx.path.get_bld().make_node('qemu_micro_flash.bin')
- qemu_spi_flash = ctx.path.get_bld().make_node('qemu_spi_flash.bin')
- qemu_spi_type = ctx.get_qemu_extflash_device_type()
- if not qemu_spi_type:
- raise Exception("External flash type for '{}' not specified".format(ctx.env.BOARD))
- machine_dep_args = ['-machine', qemu_machine,
- '-cpu', ctx.get_qemu_cpu(),
- '-pflash', qemu_micro_flash.path_from(ctx.path),
- qemu_spi_type, qemu_spi_flash.path_from(ctx.path)]
- if ctx.has_touch():
- machine_dep_args.append('-show-cursor')
- qemu_bin = os.getenv("PEBBLE_QEMU_BIN")
- if not qemu_bin or not (os.path.isfile(qemu_bin) and os.access(qemu_bin, os.X_OK)):
- qemu_bin = 'qemu-system-arm'
-
- cmd_line = (
- qemu_bin + " "
- "-rtc base=localtime "
- "-monitor stdio "
- "-s "
- "-serial file:uart1.log "
- "-serial tcp::12344,server,nowait " # Used for bluetooth data
- "-serial tcp::12345,server,nowait " # Used for console
- ) + ' '.join(machine_dep_args)
- os.system(cmd_line)
- class QEMUConsoleCommand(BuildContext):
- cmd = 'qemu_console'
- fun = 'qemu_console'
- def qemu_console(ctx):
- """Starts miniterm configured to talk to the emulator (qemu)"""
- # miniterm is not made to be used as a python module, so just shell out:
- host_port = ctx.options.qemu_host or 'localhost:12345'
- # A hacky way to pass an argument
- if _is_pulse_everywhere(ctx):
- os.system("python ./tools/pulse_console.py -t %s" % ('socket://%s' % (host_port)))
- else:
- os.system("python ./tools/log_hashing/miniterm_co.py %s" % ('socket://%s' % (host_port)))
- class QemuGdb(BuildContext):
- """Starts up a gdb instance to talk to the emulator """
- cmd = 'qemu_gdb'
- fun = 'qemu_gdb'
- def qemu_gdb(ctx):
- # First, startup the gdb proxy
- cmd_line = "python ./tools/qemu/qemu_gdb_proxy.py --port=1233 --target=localhost:1234"
- proc = pexpect.spawn(cmd_line, logfile=sys.stdout, encoding='utf-8')
- proc.expect(["Connected to target", pexpect.TIMEOUT], timeout=10)
- fw_elf = ctx.get_tintin_fw_node().change_ext('.elf')
- run_arm_gdb(ctx, fw_elf, target_server_port=1233)
- class QemuGdbBoot(BuildContext):
- """ Starts up a gdb instance to talk to the emulator's boot ROM """
- cmd = 'qemu_gdb_boot'
- fun = 'qemu_gdb_boot'
- def qemu_gdb_boot(ctx):
- boot_elf = ctx.get_tintin_boot_node().change_ext('.elf')
- run_arm_gdb(ctx, boot_elf, target_server_port=1234)
- class debug(BuildContext):
- """ Alias for gdb """
- cmd = 'debug'
- def execute_build(ctx):
- gdb(ctx)
- class Gdb(BuildContext):
- """ Starts GDB and openocd (if not already running) and attaches GDB to
- openocd's GDB server. If openocd is already running, it will be used.
- """
- cmd = 'gdb'
- fun = 'gdb'
- def gdb(ctx, fw_elf=None, cfg_file='openocd.cfg', is_ble=False):
- if fw_elf is None:
- fw_elf = ctx.get_tintin_fw_node().change_ext('.elf')
- with waftools.openocd.daemon(ctx, cfg_file,
- use_swd=(is_ble or 'swd' in ctx.env.JTAG)):
- run_arm_gdb(ctx, fw_elf, cmd_str='--init-command=".gdbinit"')
- class gdb_prf(BuildContext):
- """same as `gdb`, but loading the PRF elf instead"""
- cmd = 'gdb_prf'
- def execute_build(ctx):
- gdb(ctx, ctx.get_tintin_fw_node_prf().change_ext('.elf'))
- def openocd(ctx):
- """ Starts openocd and leaves it running. It will reset the board to
- increase the chances of attaching succesfully. """
- waftools.openocd.run_command(ctx, 'init; reset', shutdown=False)
- # Image commands
- #################################################
- def _get_dbgserial_tty():
- import pebble_tty
- tty = pebble_tty.find_dbgserial_tty()
- if tty is None:
- return None
- waflib.Logs.pprint('GREEN', 'No --tty argument specified, auto-selecting: %s' % tty)
- return tty
- class ble_send_hci(BuildContext):
- """Puts MCU in HCI bypass mode. Sends specified HCI Command and returns result. i.e:
- ./waf send_hci 0x01 0x03 0x0C 0x00
- """
- cmd = 'ble_send_hci'
- fun = 'ble_send_hci'
- def ble_send_hci(ctx):
- import prompt
- import pebble_tty
- from serial_port_wrapper import SerialPortWrapper
- import struct
- from time import sleep
- from waflib import Options
- def _dump_hex_array(prefix, hex_array):
- print(prefix + " [")
- for i in range(0, len(hex_array)):
- print("0x%02x " % hex_array[i])
- print("]")
- hci_bytes = [int(i, 16) for i in Options.commands]
- _dump_hex_array("Sent HCI CMD:", hci_bytes)
- try:
- device_tty = pebble_tty.find_dbgserial_tty()
- serial = SerialPortWrapper(device_tty)
- prompt.go_to_prompt(serial)
- prompt.issue_command(serial, "bt test hcipass")
- sleep(0.1)
- serial.write_fast(struct.pack('B'*len(hci_bytes), *hci_bytes))
- response = serial.read()
- response = struct.unpack('%dB' % len(response), response)
- serial.write(struct.pack('B', 0x04)) # issue ctrl-d
- _dump_hex_array(" Got HCI EVT:", response)
- finally:
- # note: random bytes get dropped on subsequent usb ops if you forget to close!
- serial.close()
- # WAF/optparse does not have native support for adding sub-command options
- # or variable length options. Reset the options list to prevent innocuous
- # messaging about unrecognized commands
- Options.commands = []
- return None
- class ImageResources(BuildContext):
- """flashes resources"""
- cmd = 'image_resources'
- fun = 'image_resources'
- def _is_pulse_everywhere(ctx):
- return "PULSE_EVERYWHERE=1" in ctx.env["DEFINES"]
- def _get_pulse_flash_tool(ctx):
- if _is_pulse_everywhere(ctx):
- return "pulse_flash_imaging"
- else:
- return "pulse_legacy_flash_imaging"
- def image_resources(ctx):
- tty = ctx.options.tty or _get_dbgserial_tty()
- if tty is None:
- waflib.Logs.pprint('RED', 'Error: --tty not specified')
- return
- tool_name = _get_pulse_flash_tool(ctx)
- pbpack_path = ctx.get_pbpack_node().abspath()
- waflib.Logs.pprint('CYAN', 'Writing pbpack "%s" to tty %s' % (pbpack_path, tty))
- ret = os.system("python ./tools/%s.py -t %s -p resources %s" % (tool_name, tty, pbpack_path))
- if ret != 0:
- ctx.fatal('Imaging failed')
- class ImageRecovery(BuildContext):
- """flashes recovery firmware"""
- cmd = 'image_recovery'
- fun = 'image_recovery'
- def image_recovery(ctx):
- tty = ctx.options.tty or _get_dbgserial_tty()
- if tty is None:
- waflib.Logs.pprint('RED', 'Error: --tty not specified')
- return
- tool_name = _get_pulse_flash_tool(ctx)
- recovery_bin_path = ctx.options.file or ctx.get_tintin_fw_node_prf().path_from(ctx.path)
- waflib.Logs.pprint('CYAN', 'Writing recovery bin "%s" to tty %s' % (recovery_bin_path, tty))
- ret = os.system("python ./tools/%s.py -t %s -p firmware %s" % (tool_name, tty, recovery_bin_path))
- if ret != 0:
- ctx.fatal('Imaging failed')
- # Flash commands
- #################################################
- class FirmwareTooLargeException(Exception):
- pass
- def _check_firmware_image_size(ctx, path):
- BYTES_PER_K = 1024
- firmware_size = os.path.getsize(path)
- # Determine flash and bootloader size so we can calculate the max firmware size
- if ctx.env.MICRO_FAMILY == 'STM32F2':
- # 512k of flash and 16k bootloader
- max_firmware_size = (512 - 16) * BYTES_PER_K
- elif ctx.env.MICRO_FAMILY == 'STM32F4':
- if ctx.env.BOARD.startswith('silk') and ctx.variant == 'prf':
- # silk PRF is limited to 512k to save on SPI flash space
- max_firmware_size = 512 * BYTES_PER_K
- elif ctx.env.BOARD in ('snowy_evt', 'snowy_evt2', 'spalding_evt'):
- # 1024k of flash and 64k bootloader
- max_firmware_size = (1024 - 64) * BYTES_PER_K
- else:
- # 1024k of flash and 16k bootloader
- max_firmware_size = (1024 - 16) * BYTES_PER_K
- elif ctx.env.MICRO_FAMILY == 'STM32F7':
- if ctx.variant == 'prf' and not ctx.env.IS_MFG:
- # Robert PRF is limited to 512k to save on SPI flash space
- max_firmware_size = 512 * BYTES_PER_K
- else:
- # 2048k of flash and 32k bootloader
- max_firmware_size = (2048 - 32) * BYTES_PER_K
- elif ctx.env.MICRO_FAMILY == 'NRF52840':
- if ctx.variant == 'prf' and not ctx.env.IS_MFG:
- max_firmware_size = 512 * BYTES_PER_K
- else:
- # 1024k of flash and 32k bootloader
- max_firmware_size = (1024 - 32) * BYTES_PER_K
- else:
- ctx.fatal('Cannot check firmware size against unknown micro family "{}"'
- .format(ctx.env.MICRO_FAMILY))
- if firmware_size > max_firmware_size:
- raise FirmwareTooLargeException('Firmware is too large! Size is 0x%x should be less than 0x%x' \
- % (firmware_size, max_firmware_size))
- return ('%d / %d bytes used (%d free)' %
- (firmware_size, max_firmware_size, (max_firmware_size - firmware_size)))
- class FlashCommand(BuildContext):
- """alias for flash_everything"""
- cmd = 'flash'
- fun = 'flash'
- def flash(ctx):
- flash_everything(ctx, ctx.get_tintin_fw_node())
- class FlashPrfCommand(BuildContext):
- """flashes recovery firmware as normal firmware"""
- cmd = 'flash_prf'
- fun = 'flash_prf'
- def flash_prf(ctx):
- flash_everything(ctx, ctx.get_tintin_fw_node_prf())
- class FlashBootCommand(BuildContext):
- cmd = 'flash_boot'
- fun = 'flash_boot'
- def flash_boot(ctx):
- """flashes a bootloader"""
- if not ctx.env.BOOTLOADER_HEX:
- ctx.fatal("Target does not have a bootloader binary available")
- waftools.openocd.run_command(ctx, 'init; reset halt; ' +
- 'program {} reset;'.format(ctx.env.BOOTLOADER_HEX),
- expect=["Programming Finished"],
- enforce_expect=True)
- class FlashFirmware(BuildContext):
- """flashes a firmware"""
- cmd = 'flash_fw'
- def execute_build(ctx):
- flash_fw(ctx, ctx.get_tintin_fw_node())
- def flash_fw(ctx, fw_bin):
- _check_firmware_image_size(ctx, fw_bin.path_from(ctx.path))
- hex_path = fw_bin.change_ext('.hex').path_from(ctx.path)
- waftools.openocd.run_command(ctx, 'init; reset halt; ' +
- 'program {} reset;'.format(hex_path),
- expect=["Programming Finished"],
- enforce_expect=True)
- def flash_everything(ctx, fw_bin):
- """flashes a bootloader and firmware"""
- if ctx.env.QEMU:
- ctx.fatal("I'm sorry Dave, I can't let you do that.\n"
- "QEMU firmwares do not work on physical hardware.\n"
- "Configure without --qemu and rebuild before trying again.")
- _check_firmware_image_size(ctx, fw_bin.path_from(ctx.path))
- if not ctx.env.BOOTLOADER_HEX:
- ctx.fatal("Target does not have a bootloader binary available")
- hex_path = fw_bin.change_ext('.hex').path_from(ctx.path)
- waftools.openocd.run_command(ctx, 'init; reset halt; '
- 'program {};'.format(ctx.env.BOOTLOADER_HEX) +
- 'program {} reset;'.format(hex_path),
- expect=["Programming Finished", "Programming Finished", "shutdown"],
- enforce_expect=True)
- def force_flash(ctx):
- """forces a connected device into a flashing state"""
- reset_config = waftools.openocd._get_reset_conf(ctx, True)
- reset_cmd = "reset_config %s; " % reset_config
- waftools.openocd.run_command(ctx, reset_cmd + 'init; reset halt;', ignore_fail=True)
- waftools.openocd.run_command(ctx, reset_cmd + 'init; stm32x unlock 0;', ignore_fail=True)
- def reset(ctx):
- """resets a connected device"""
- waftools.openocd.run_command(ctx, 'init; reset;', expect=["found"])
- def bork(ctx):
- """resets and wipes a connected a device"""
- waftools.openocd.run_command(ctx, 'init; reset halt;', ignore_fail=True)
- waftools.openocd.run_command(ctx, 'init; flash erase_sector 0 0 1;', ignore_fail=True)
- def make_lang(ctx):
- """generate translation files and update existing ones"""
- ctx.recurse('resources/normal/base/lang')
- class PackLangCommand(BuildContext):
- cmd = 'pack_lang'
- fun = 'pack_lang'
- def pack_lang(ctx):
- """generates pbpack for langs"""
- ctx.recurse('resources/normal/base/lang')
- class PackAllLangsCommand(BuildContext):
- cmd = 'pack_all_langs'
- fun = 'pack_all_langs'
- def pack_all_langs(ctx):
- """generates pbpack for all langs"""
- ctx.recurse('resources/normal/base/lang')
- # Tool build commands
- #################################################
- class build_pdc2png(BuildContext):
- """executes the pdc2png build"""
- cmd = 'build_pdc2png'
- variant = 'pdc2png'
- class build_tools(BuildContext):
- """build all tools in tools/ dir"""
- cmd = 'build_tools'
- variant = 'tools'
- # vim:filetype=python
|