scripts: refactor flash/debug scripts to remove "shell"

The Python-based runners have replaced the old shell scripts. Refactor
the build system accordingly:

- FLASH_SCRIPT is now BOARD_FLASH_RUNNER
- DEBUG_SCRIPT is now BOARD_DEBUG_RUNNER

The values, rather than being the names of files, are now the names of
runners in scripts/support/runner. They are still short, descriptive
names like "openocd", "jlink", "em-starterkit", etc.

Adjust the zephyr_flash_debug.py call and runner internals
accordingly. Have each runner class report a name and the commands it
can handle. This lets us move some boilerplate from each do_run()
method into the common run() routine, and enables further improvements
in future patches.

The handles_command() method is temporary, and will be replaced by a
more general mechanism for describing runner capabilities in a
subsequent patch. The initial use case for extending this is to add
device tree awareness to the runners.

To try to avoid user confusion, abort the configuration if an
xxx_SCRIPT is defined.

Signed-off-by: Marti Bolivar <marti@opensourcefoundries.com>
diff --git a/boards/arc/arduino_101_sss/board.cmake b/boards/arc/arduino_101_sss/board.cmake
index 7f11e3a..b6b06e8 100644
--- a/boards/arc/arduino_101_sss/board.cmake
+++ b/boards/arc/arduino_101_sss/board.cmake
@@ -1,5 +1,5 @@
 if(DEFINED ENV{ZEPHYR_FLASH_OVER_DFU})
-  set(FLASH_SCRIPT dfuutil.sh)
+  set(BOARD_FLASH_RUNNER dfu-util)
 
   set(DFUUTIL_PID 8087:0aba)
   set(DFUUTIL_ALT sensor_core)
@@ -11,10 +11,10 @@
     DFUUTIL_IMG
     )
 else()
-  set(FLASH_SCRIPT openocd.sh)
+  set(BOARD_FLASH_RUNNER openocd)
 endif()
 
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_DEBUG_RUNNER openocd)
 
 set(OPENOCD_PRE_CMD "targets 1")
 set(OPENOCD_LOAD_CMD "load_image     ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
diff --git a/boards/arc/em_starterkit/board.cmake b/boards/arc/em_starterkit/board.cmake
index dbfe993..8af72db 100644
--- a/boards/arc/em_starterkit/board.cmake
+++ b/boards/arc/em_starterkit/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT arc_debugger.sh)
-set(DEBUG_SCRIPT arc_debugger.sh)
+set(BOARD_FLASH_RUNNER em-starterkit)
+set(BOARD_DEBUG_RUNNER em-starterkit)
 
 set(OPENOCD_LOAD_CMD "load_image     ${PROJECT_BINARY_DIR}/${KERNEL_ELF_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
 set(OPENOCD_VERIFY_CMD "verify_image ${PROJECT_BINARY_DIR}/${KERNEL_ELF_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
diff --git a/boards/arm/96b_carbon/board.cmake b/boards/arm/96b_carbon/board.cmake
index 4564545..055fbc9 100644
--- a/boards/arm/96b_carbon/board.cmake
+++ b/boards/arm/96b_carbon/board.cmake
@@ -1,4 +1,4 @@
-set(FLASH_SCRIPT dfuutil.sh)
+set(BOARD_FLASH_RUNNER dfu-util)
 
 set(DFUUTIL_PID 0483:df11)
 set(DFUUTIL_ALT 0)
diff --git a/boards/arm/96b_nitrogen/board.cmake b/boards/arm/96b_nitrogen/board.cmake
index 973c2c6..419d19a 100644
--- a/boards/arm/96b_nitrogen/board.cmake
+++ b/boards/arm/96b_nitrogen/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT pyocd.sh)
-set(DEBUG_SCRIPT pyocd.sh)
+set(BOARD_FLASH_RUNNER pyocd)
+set(BOARD_DEBUG_RUNNER pyocd)
 
 set(PYOCD_TARGET nrf52)
 
diff --git a/boards/arm/arduino_101_ble/board.cmake b/boards/arm/arduino_101_ble/board.cmake
index 56b69f0..73aef2b 100644
--- a/boards/arm/arduino_101_ble/board.cmake
+++ b/boards/arm/arduino_101_ble/board.cmake
@@ -1,5 +1,5 @@
 if(DEFINED ENV{ZEPHYR_FLASH_OVER_DFU})
-  set(FLASH_SCRIPT dfuutil.sh)
+  set(BOARD_FLASH_RUNNER dfu-util)
 
   set(DFUUTIL_PID 8087:0aba)
   set(DFUUTIL_ALT ble_core)
diff --git a/boards/arm/arduino_due/board.cmake b/boards/arm/arduino_due/board.cmake
index 8e4fb33..264e10d 100644
--- a/boards/arm/arduino_due/board.cmake
+++ b/boards/arm/arduino_due/board.cmake
@@ -1 +1 @@
-set(FLASH_SCRIPT bossa-flash.sh)
+set(BOARD_FLASH_RUNNER bossac)
diff --git a/boards/arm/bbc_microbit/board.cmake b/boards/arm/bbc_microbit/board.cmake
index 15f697a..15a2fb0 100644
--- a/boards/arm/bbc_microbit/board.cmake
+++ b/boards/arm/bbc_microbit/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT pyocd.sh)
-set(DEBUG_SCRIPT pyocd.sh)
+set(BOARD_FLASH_RUNNER pyocd)
+set(BOARD_DEBUG_RUNNER pyocd)
 
 set(PYOCD_TARGET nrf51)
 
diff --git a/boards/arm/frdm_k64f/board.cmake b/boards/arm/frdm_k64f/board.cmake
index aadbdb6..bde2ef4 100644
--- a/boards/arm/frdm_k64f/board.cmake
+++ b/boards/arm/frdm_k64f/board.cmake
@@ -1,10 +1,10 @@
 set_ifndef(OPENSDA_FW daplink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 elseif(OPENSDA_FW STREQUAL daplink)
-  set_ifndef(DEBUG_SCRIPT pyocd.sh)
-  set_ifndef(FLASH_SCRIPT pyocd.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER pyocd)
+  set_ifndef(BOARD_FLASH_RUNNER pyocd)
 endif()
 
 set(JLINK_DEVICE MK64FN1M0xxx12)
diff --git a/boards/arm/frdm_kl25z/board.cmake b/boards/arm/frdm_kl25z/board.cmake
index f2c955a..e794ba4 100644
--- a/boards/arm/frdm_kl25z/board.cmake
+++ b/boards/arm/frdm_kl25z/board.cmake
@@ -1,10 +1,10 @@
 set_ifndef(OPENSDA_FW daplink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 elseif(OPENSDA_FW STREQUAL daplink)
-  set_ifndef(DEBUG_SCRIPT pyocd.sh)
-  set_ifndef(FLASH_SCRIPT pyocd.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER pyocd)
+  set_ifndef(BOARD_FLASH_RUNNER pyocd)
 endif()
 
 set(JLINK_DEVICE MKL25Z128xxx4)
diff --git a/boards/arm/frdm_kw41z/board.cmake b/boards/arm/frdm_kw41z/board.cmake
index c08cd74..0d8876c 100644
--- a/boards/arm/frdm_kw41z/board.cmake
+++ b/boards/arm/frdm_kw41z/board.cmake
@@ -1,10 +1,10 @@
 set_ifndef(OPENSDA_FW jlink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 elseif(OPENSDA_FW STREQUAL daplink)
-  set_ifndef(DEBUG_SCRIPT pyocd.sh)
-  set_ifndef(FLASH_SCRIPT pyocd.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER pyocd)
+  set_ifndef(BOARD_FLASH_RUNNER pyocd)
 endif()
 
 set(JLINK_DEVICE MKW41Z512xxx4)
diff --git a/boards/arm/hexiwear_k64/board.cmake b/boards/arm/hexiwear_k64/board.cmake
index 6e143a5..2d4b0d2 100644
--- a/boards/arm/hexiwear_k64/board.cmake
+++ b/boards/arm/hexiwear_k64/board.cmake
@@ -1,10 +1,10 @@
 set_ifndef(OPENSDA_FW daplink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 elseif(OPENSDA_FW STREQUAL daplink)
-  set_ifndef(DEBUG_SCRIPT pyocd.sh)
-  set_ifndef(FLASH_SCRIPT pyocd.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER pyocd)
+  set_ifndef(BOARD_FLASH_RUNNER pyocd)
 endif()
 
 set(JLINK_DEVICE MK64FN1M0xxx12)
diff --git a/boards/arm/hexiwear_kw40z/board.cmake b/boards/arm/hexiwear_kw40z/board.cmake
index 120eda8..bff8044 100644
--- a/boards/arm/hexiwear_kw40z/board.cmake
+++ b/boards/arm/hexiwear_kw40z/board.cmake
@@ -1,10 +1,10 @@
 set_ifndef(OPENSDA_FW jlink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 elseif(OPENSDA_FW STREQUAL daplink)
-  set_ifndef(DEBUG_SCRIPT pyocd.sh)
-  set_ifndef(FLASH_SCRIPT pyocd.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER pyocd)
+  set_ifndef(BOARD_FLASH_RUNNER pyocd)
 endif()
 
 set(JLINK_DEVICE MKW40Z160xxx4)
diff --git a/boards/arm/mimxrt1050_evk/board.cmake b/boards/arm/mimxrt1050_evk/board.cmake
index 51ed8f7..8e6f095 100644
--- a/boards/arm/mimxrt1050_evk/board.cmake
+++ b/boards/arm/mimxrt1050_evk/board.cmake
@@ -7,7 +7,7 @@
 set_ifndef(OPENSDA_FW jlink)
 
 if(OPENSDA_FW STREQUAL jlink)
-  set_ifndef(DEBUG_SCRIPT jlink.sh)
+  set_ifndef(BOARD_DEBUG_RUNNER jlink)
 endif()
 
 set(JLINK_DEVICE Cortex-M7)
diff --git a/boards/arm/nrf51_pca10028/board.cmake b/boards/arm/nrf51_pca10028/board.cmake
index b846ba3..76f17e0 100644
--- a/boards/arm/nrf51_pca10028/board.cmake
+++ b/boards/arm/nrf51_pca10028/board.cmake
@@ -1,4 +1,4 @@
-set(FLASH_SCRIPT nrf_flash.sh)
+set(BOARD_FLASH_RUNNER nrfjprog)
 
 set(NRF_FAMILY NRF51)
 
diff --git a/boards/arm/nrf51_vbluno51/board.cmake b/boards/arm/nrf51_vbluno51/board.cmake
index 15f697a..15a2fb0 100644
--- a/boards/arm/nrf51_vbluno51/board.cmake
+++ b/boards/arm/nrf51_vbluno51/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT pyocd.sh)
-set(DEBUG_SCRIPT pyocd.sh)
+set(BOARD_FLASH_RUNNER pyocd)
+set(BOARD_DEBUG_RUNNER pyocd)
 
 set(PYOCD_TARGET nrf51)
 
diff --git a/boards/arm/nrf52840_pca10056/board.cmake b/boards/arm/nrf52840_pca10056/board.cmake
index a9bde07..623dacf 100644
--- a/boards/arm/nrf52840_pca10056/board.cmake
+++ b/boards/arm/nrf52840_pca10056/board.cmake
@@ -1,4 +1,4 @@
-set(FLASH_SCRIPT nrf_flash.sh)
+set(BOARD_FLASH_RUNNER nrfjprog)
 set(NRF_FAMILY NRF52)
 
 set_property(GLOBAL APPEND PROPERTY FLASH_SCRIPT_ENV_VARS
diff --git a/boards/arm/nrf52_blenano2/board.cmake b/boards/arm/nrf52_blenano2/board.cmake
index 973c2c6..419d19a 100644
--- a/boards/arm/nrf52_blenano2/board.cmake
+++ b/boards/arm/nrf52_blenano2/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT pyocd.sh)
-set(DEBUG_SCRIPT pyocd.sh)
+set(BOARD_FLASH_RUNNER pyocd)
+set(BOARD_DEBUG_RUNNER pyocd)
 
 set(PYOCD_TARGET nrf52)
 
diff --git a/boards/arm/nrf52_pca10040/board.cmake b/boards/arm/nrf52_pca10040/board.cmake
index cc3096b..0130656 100644
--- a/boards/arm/nrf52_pca10040/board.cmake
+++ b/boards/arm/nrf52_pca10040/board.cmake
@@ -1,4 +1,4 @@
-set(FLASH_SCRIPT nrf_flash.sh)
+set(BOARD_FLASH_RUNNER nrfjprog)
 
 set(NRF_FAMILY NRF52)
 
diff --git a/boards/arm/nrf52_vbluno52/board.cmake b/boards/arm/nrf52_vbluno52/board.cmake
index 973c2c6..419d19a 100644
--- a/boards/arm/nrf52_vbluno52/board.cmake
+++ b/boards/arm/nrf52_vbluno52/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT pyocd.sh)
-set(DEBUG_SCRIPT pyocd.sh)
+set(BOARD_FLASH_RUNNER pyocd)
+set(BOARD_DEBUG_RUNNER pyocd)
 
 set(PYOCD_TARGET nrf52)
 
diff --git a/boards/arm/qemu_cortex_m3/board.cmake b/boards/arm/qemu_cortex_m3/board.cmake
index 2f7f023..8aedad6 100644
--- a/boards/arm/qemu_cortex_m3/board.cmake
+++ b/boards/arm/qemu_cortex_m3/board.cmake
@@ -8,4 +8,4 @@
   -vga none
   )
 
-set(DEBUG_SCRIPT qemu.sh)
+set(BOARD_DEBUG_RUNNER qemu)
diff --git a/boards/arm/usb_kw24d512/board.cmake b/boards/arm/usb_kw24d512/board.cmake
index f1bf3182..bf1424e 100644
--- a/boards/arm/usb_kw24d512/board.cmake
+++ b/boards/arm/usb_kw24d512/board.cmake
@@ -1,4 +1,4 @@
-set(DEBUG_SCRIPT jlink.sh)
+set(BOARD_DEBUG_RUNNER jlink)
 
 set(JLINK_DEVICE MKW24D512xxx5)
 
diff --git a/boards/arm/v2m_beetle/board.cmake b/boards/arm/v2m_beetle/board.cmake
index ca2c812..cf3657f 100644
--- a/boards/arm/v2m_beetle/board.cmake
+++ b/boards/arm/v2m_beetle/board.cmake
@@ -1 +1 @@
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_DEBUG_RUNNER openocd)
diff --git a/boards/common/openocd.board.cmake b/boards/common/openocd.board.cmake
index c032653..b2a31b2 100644
--- a/boards/common/openocd.board.cmake
+++ b/boards/common/openocd.board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT openocd.sh)
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_FLASH_RUNNER openocd)
+set(BOARD_DEBUG_RUNNER openocd)
 
 set(OPENOCD_LOAD_CMD   "flash write_image erase ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
 set(OPENOCD_VERIFY_CMD "verify_image            ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
diff --git a/boards/nios2/altera_max10/board.cmake b/boards/nios2/altera_max10/board.cmake
index 46e36a3..ad4d1d6 100644
--- a/boards/nios2/altera_max10/board.cmake
+++ b/boards/nios2/altera_max10/board.cmake
@@ -1,4 +1,4 @@
-set(FLASH_SCRIPT nios2.sh)
-set(DEBUG_SCRIPT nios2.sh)
+set(BOARD_FLASH_RUNNER nios2)
+set(BOARD_DEBUG_RUNNER nios2)
 set(NIOS2_CPU_SOF $ENV{ZEPHYR_BASE}/arch/nios2/soc/nios2f-zephyr/cpu/ghrd_10m50da.sof)
 set_property(GLOBAL APPEND PROPERTY FLASH_SCRIPT_ENV_VARS NIOS2_CPU_SOF)
diff --git a/boards/nios2/qemu_nios2/board.cmake b/boards/nios2/qemu_nios2/board.cmake
index 904d679..c4beaf5 100644
--- a/boards/nios2/qemu_nios2/board.cmake
+++ b/boards/nios2/qemu_nios2/board.cmake
@@ -7,4 +7,4 @@
   -nographic
   )
 
-set(DEBUG_SCRIPT qemu.sh)
+set(BOARD_DEBUG_RUNNER qemu)
diff --git a/boards/riscv32/qemu_riscv32/board.cmake b/boards/riscv32/qemu_riscv32/board.cmake
index ab1d27c..89f300e 100644
--- a/boards/riscv32/qemu_riscv32/board.cmake
+++ b/boards/riscv32/qemu_riscv32/board.cmake
@@ -8,4 +8,4 @@
   -machine sifive
   )
 
-set(DEBUG_SCRIPT qemu.sh)
+set(BOARD_DEBUG_RUNNER qemu)
diff --git a/boards/x86/arduino_101/board.cmake b/boards/x86/arduino_101/board.cmake
index 0ec9b0f..98ace6b 100644
--- a/boards/x86/arduino_101/board.cmake
+++ b/boards/x86/arduino_101/board.cmake
@@ -1,5 +1,5 @@
 if(DEFINED ENV{ZEPHYR_FLASH_OVER_DFU})
-  set(FLASH_SCRIPT dfuutil.sh)
+  set(BOARD_FLASH_RUNNER dfu-util)
 
   set(DFUUTIL_PID 8087:0aba)
   set(DFUUTIL_ALT x86_app)
@@ -11,10 +11,10 @@
     DFUUTIL_IMG
     )
 else()
-  set(FLASH_SCRIPT openocd.sh)
+  set(BOARD_FLASH_RUNNER openocd)
 endif()
 
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_DEBUG_RUNNER openocd)
 
 set(OPENOCD_PRE_CMD "targets 1")
 set(OPENOCD_LOAD_CMD "load_image     ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
diff --git a/boards/x86/panther/board.cmake b/boards/x86/panther/board.cmake
index d43f3f6..e21d41e 100644
--- a/boards/x86/panther/board.cmake
+++ b/boards/x86/panther/board.cmake
@@ -1,5 +1,5 @@
-set(FLASH_SCRIPT openocd.sh)
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_FLASH_RUNNER openocd)
+set(BOARD_DEBUG_RUNNER openocd)
 
 set(OPENOCD_PRE_CMD "targets 1")
 set(OPENOCD_LOAD_CMD "load_image     ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_PHYS_LOAD_ADDR}")
diff --git a/boards/x86/qemu_x86/board.cmake b/boards/x86/qemu_x86/board.cmake
index cce924f..c84261f 100644
--- a/boards/x86/qemu_x86/board.cmake
+++ b/boards/x86/qemu_x86/board.cmake
@@ -23,6 +23,6 @@
   )
 
 # TODO: Support debug
-# set(DEBUG_SCRIPT qemu.sh)
+# set(BOARD_DEBUG_RUNNER qemu)
 # debugserver: QEMU_EXTRA_FLAGS += -s -S
 # debugserver: qemu
diff --git a/boards/x86/tinytile/board.cmake b/boards/x86/tinytile/board.cmake
index 0ec9b0f..98ace6b 100644
--- a/boards/x86/tinytile/board.cmake
+++ b/boards/x86/tinytile/board.cmake
@@ -1,5 +1,5 @@
 if(DEFINED ENV{ZEPHYR_FLASH_OVER_DFU})
-  set(FLASH_SCRIPT dfuutil.sh)
+  set(BOARD_FLASH_RUNNER dfu-util)
 
   set(DFUUTIL_PID 8087:0aba)
   set(DFUUTIL_ALT x86_app)
@@ -11,10 +11,10 @@
     DFUUTIL_IMG
     )
 else()
-  set(FLASH_SCRIPT openocd.sh)
+  set(BOARD_FLASH_RUNNER openocd)
 endif()
 
-set(DEBUG_SCRIPT openocd.sh)
+set(BOARD_DEBUG_RUNNER openocd)
 
 set(OPENOCD_PRE_CMD "targets 1")
 set(OPENOCD_LOAD_CMD "load_image     ${PROJECT_BINARY_DIR}/${KERNEL_BIN_NAME} ${CONFIG_FLASH_BASE_ADDRESS}")
diff --git a/boards/x86/x86_jailhouse/board.cmake b/boards/x86/x86_jailhouse/board.cmake
index 585b8fb..a27fb7b 100644
--- a/boards/x86/x86_jailhouse/board.cmake
+++ b/boards/x86/x86_jailhouse/board.cmake
@@ -35,6 +35,6 @@
   )
 
 # TODO: Support debug
-# set(DEBUG_SCRIPT qemu.sh)
+# set(BOARD_DEBUG_RUNNER qemu)
 # debugserver: QEMU_EXTRA_FLAGS += -s -S
 # debugserver: qemu
diff --git a/boards/xtensa/esp32/board.cmake b/boards/xtensa/esp32/board.cmake
index 8ce6887..52d3449 100644
--- a/boards/xtensa/esp32/board.cmake
+++ b/boards/xtensa/esp32/board.cmake
@@ -1 +1 @@
-set(FLASH_SCRIPT esp32.sh)
+set(BOARD_FLASH_RUNNER esp32)
diff --git a/boards/xtensa/qemu_xtensa/board.cmake b/boards/xtensa/qemu_xtensa/board.cmake
index 738b3c1..0899224 100644
--- a/boards/xtensa/qemu_xtensa/board.cmake
+++ b/boards/xtensa/qemu_xtensa/board.cmake
@@ -7,6 +7,6 @@
   )
 
 # TODO: Support debug
-# set(DEBUG_SCRIPT qemu.sh)
+# set(BOARD_DEBUG_RUNNER qemu)
 # debugserver: QEMU_EXTRA_FLAGS += -s -S
 # debugserver: qemu
diff --git a/boards/xtensa/xt-sim/board.cmake b/boards/xtensa/xt-sim/board.cmake
index 4f34b14..409a3cb 100644
--- a/boards/xtensa/xt-sim/board.cmake
+++ b/boards/xtensa/xt-sim/board.cmake
@@ -1 +1 @@
-set(DEBUG_SCRIPT xt-gdb.sh)
+set(BOARD_DEBUG_RUNNER xtensa)
diff --git a/cmake/app/boilerplate.cmake b/cmake/app/boilerplate.cmake
index b35afc7..484c211 100644
--- a/cmake/app/boilerplate.cmake
+++ b/cmake/app/boilerplate.cmake
@@ -246,7 +246,7 @@
 set_property(GLOBAL PROPERTY GENERATED_KERNEL_SOURCE_FILES "")
 
 define_property(GLOBAL PROPERTY FLASH_SCRIPT_ENV_VARS
-  BRIEF_DOCS "Environment variables that should be passed to FLASH_SCRIPT"
-  FULL_DOCS  "Environment variables that should be passed to FLASH_SCRIPT"
+  BRIEF_DOCS "Environment variables to pass to the flash/debug script"
+  FULL_DOCS  "Environment variables to pass to the flash/debug script"
   )
 set_property(GLOBAL PROPERTY GENERATED_KERNEL_SOURCE_FILES "")
diff --git a/cmake/flash/CMakeLists.txt b/cmake/flash/CMakeLists.txt
index d28b750..90e067f 100644
--- a/cmake/flash/CMakeLists.txt
+++ b/cmake/flash/CMakeLists.txt
@@ -1,3 +1,6 @@
+assert_not(FLASH_SCRIPT "FLASH_SCRIPT has been removed; use BOARD_FLASH_RUNNER")
+assert_not(DEBUG_SCRIPT "DEBUG_SCRIPT has been removed; use BOARD_DEBUG_RUNNER")
+
 get_property(ENV_VARS GLOBAL PROPERTY FLASH_SCRIPT_ENV_VARS)
 
 set(ENV_VARS_FORMATTED "")
@@ -21,13 +24,13 @@
 foreach(target flash debug debugserver)
   if(target STREQUAL flash)
     set(comment "Flashing ${BOARD}")
-    set(script ${FLASH_SCRIPT})
+    set(runner ${BOARD_FLASH_RUNNER})
   elseif(target STREQUAL debug)
     set(comment "Debugging ${BOARD}")
-    set(script ${DEBUG_SCRIPT})
+    set(runner ${BOARD_DEBUG_RUNNER})
   elseif(target STREQUAL debugserver)
     set(comment "Debugging ${BOARD}")
-    set(script ${DEBUG_SCRIPT})
+    set(runner ${BOARD_DEBUG_RUNNER})
     if(EMU_PLATFORM)
       # cmake/qemu/CMakeLists.txt will add a debugserver target for
       # emulation platforms, so we don't add one here
@@ -35,14 +38,14 @@
     endif()
   endif()
 
-  if(script)
+  if(runner)
     set(cmd
       ${CMAKE_COMMAND} -E env
       ${ENV_VARS_FORMATTED}
       ${PYTHON_EXECUTABLE}
       $ENV{ZEPHYR_BASE}/scripts/support/zephyr_flash_debug.py
+      ${runner}
       ${target}
-      ${script}
       DEPENDS ${logical_target_for_zephyr_elf}
       WORKING_DIRECTORY ${APPLICATION_BINARY_DIR}
       )
diff --git a/scripts/support/runner/arc.py b/scripts/support/runner/arc.py
index c3647c9..9f5e5bf 100644
--- a/scripts/support/runner/arc.py
+++ b/scripts/support/runner/arc.py
@@ -2,7 +2,7 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
-'''ARC architecture-specific runner.'''
+'''ARC architecture-specific runners.'''
 
 from os import path
 import os
@@ -15,7 +15,7 @@
 DEFAULT_ARC_GDB_PORT = 3333
 
 
-class ArcBinaryRunner(ZephyrBinaryRunner):
+class EmStarterKitBinaryRunner(ZephyrBinaryRunner):
     '''Runner front-end for the EM Starterkit board, using openocd.'''
 
     # This unusual 'flash' implementation matches the original shell script.
@@ -31,7 +31,7 @@
                  tui=None, tcl_port=DEFAULT_ARC_TCL_PORT,
                  telnet_port=DEFAULT_ARC_TELNET_PORT,
                  gdb_port=DEFAULT_ARC_GDB_PORT, debug=False):
-        super(ArcBinaryRunner, self).__init__(debug=debug)
+        super(EmStarterKitBinaryRunner, self).__init__(debug=debug)
         self.elf = elf
         self.zephyr_base = zephyr_base
         self.board_dir = board_dir
@@ -46,9 +46,9 @@
         self.telnet_port = telnet_port
         self.gdb_port = gdb_port
 
-    def replaces_shell_script(shell_script, command):
-        return (command in {'flash', 'debug', 'debugserver'} and
-                shell_script == 'arc_debugger.sh')
+    @classmethod
+    def name(cls):
+        return 'em-starterkit'
 
     def create_from_env(command, debug):
         '''Create runner from environment.
@@ -90,11 +90,12 @@
         gdb_port = int(os.environ.get('GDB_PORT',
                                       str(DEFAULT_ARC_GDB_PORT)))
 
-        return ArcBinaryRunner(elf, zephyr_base, board_dir,
-                               gdb, openocd=openocd, extra_init=extra_init,
-                               default_path=default_path, tui=tui,
-                               tcl_port=tcl_port, telnet_port=telnet_port,
-                               gdb_port=gdb_port, debug=debug)
+        return EmStarterKitBinaryRunner(
+            elf, zephyr_base, board_dir,
+            gdb, openocd=openocd, extra_init=extra_init,
+            default_path=default_path, tui=tui,
+            tcl_port=tcl_port, telnet_port=telnet_port,
+            gdb_port=gdb_port, debug=debug)
 
     def do_run(self, command, **kwargs):
         if command not in {'flash', 'debug', 'debugserver'}:
diff --git a/scripts/support/runner/bossac.py b/scripts/support/runner/bossac.py
index cbf24b4..36ada99 100644
--- a/scripts/support/runner/bossac.py
+++ b/scripts/support/runner/bossac.py
@@ -23,8 +23,13 @@
         self.bossac = bossac
         self.port = port
 
-    def replaces_shell_script(shell_script, command):
-        return command == 'flash' and shell_script == 'bossa-flash.sh'
+    @classmethod
+    def name(cls):
+        return 'bossac'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command == 'flash'
 
     def create_from_env(command, debug):
         '''Create flasher from environment.
@@ -47,9 +52,6 @@
                                   debug=debug)
 
     def do_run(self, command, **kwargs):
-        if command != 'flash':
-            raise ValueError('only flash is supported')
-
         if platform.system() != 'Linux':
             msg = 'CAUTION: No flash tool for your host system found!'
             raise NotImplementedError(msg)
diff --git a/scripts/support/runner/core.py b/scripts/support/runner/core.py
index b66b535..ad7113b 100644
--- a/scripts/support/runner/core.py
+++ b/scripts/support/runner/core.py
@@ -1,6 +1,7 @@
 #! /usr/bin/env python3
 
 # Copyright (c) 2017 Linaro Limited.
+# Copyright (c) 2017 Open Source Foundries Limited.
 #
 # SPDX-License-Identifier: Apache-2.0
 
@@ -145,12 +146,16 @@
 class ZephyrBinaryRunner(abc.ABC):
     '''Abstract superclass for binary runners (flashers, debuggers).
 
+    **Note**: these APIs are still evolving, and will change!
+
     With some exceptions, boards supported by Zephyr must provide
     generic means to be flashed (have a Zephyr firmware binary
     permanently installed on the device for running) and debugged
     (have a breakpoint debugger and program loader on a host
-    workstation attached to a running target). This is supported by
-    three top-level commands managed by the Zephyr build system:
+    workstation attached to a running target).
+
+    This is supported by three top-level commands managed by the
+    Zephyr build system:
 
     - 'flash': flash a previously configured binary to the board,
       start execution on the target, then return.
@@ -164,71 +169,103 @@
       connect to a debug server with symbol tables loaded from the
       binary.
 
-    Runner functionality relies on a variety of target-specific tools
-    and configuration values, the user interface to which is
-    abstracted by this class. Each runner subclass should take any
-    values it needs to execute one of these commands in its
-    constructor.  The actual command execution is handled in the run()
-    method.
+    This class provides an API for these commands. Every runner has a
+    name, and declares commands it can handle. Zephyr boards declare
+    compatible runner(s) by name to the build system, which can then
+    call into the create_runner() method below to make a concrete
+    runner instance for use executing a command.
 
-    This functionality has replaced the legacy Zephyr runners,
-    which were shell scripts.
+    If your board can use an existing runner, all you have to do is
+    give its name to the build system. How to do that is out of the
+    scope of this documentation, but use the existing boards as a
+    starting point.
 
-    At present, the Zephyr build system uses a variety of
-    tool-specific environment variables to control runner behavior.
-    To support a transition to ZephyrBinaryRunner and subclasses, this
-    class provides a create_for_shell_script() static factory method.
-    This method iterates over ZephyrBinaryRunner subclasses,
-    determines which (if any) can provide equivalent functionality to
-    the old shell-based runner, and returns a subclass instance with its
-    configuration determined from the environment.
+    If you want to define and use your own runner:
 
-    To support this, subclasess currently must provide a pair of
-    static methods, replaces_shell_script() and create_from_env(). The
-    first allows the runner subclass to declare which commands and
-    scripts it can replace. The second is called by
-    create_for_shell_script() to create a concrete runner instance.
+    1. Define a ZephyrBinaryRunner subclass, and implement its
+       abstract methods. Override any methods you need to, especially
+       handles_command().
+
+    2. Make sure the Python module defining your runner class is
+       imported by this package's __init__.py (otherwise,
+       create_runner() won't work).
+
+    3. Give your runner's name to the Zephyr build system in your
+       board's build files.
+
+    Runners use a variety of target-specific tools and configuration
+    values, the user interface to which is abstracted by this
+    class. Each runner subclass should take any values it needs to
+    execute one of these commands in its constructor.  The actual
+    command execution is handled in the run() method.
+
+    At present, the Zephyr build system uses environment variables to
+    control runner behavior.  To support this, a create_runner()
+    method is defined below.  This method takes a runner name, and
+    iterates over defined ZephyrBinaryRunner subclasses to find the
+    runner class. It then checks that it supports the command, then
+    instantiates and returns a runner with configuration determined by
+    the environment.
+
+    To support this, subclasses currently must define a static method:
+    create_from_env(). This is called by create_runner() to create a
+    concrete runner instance.
 
     The environment-based factories are for legacy use *only*; the
+    build system is moving away from use of environment variables. The
     user must be able to construct and use a runner using only the
-    constructor and run() method.'''
+    constructor and run() method.
+
+    '''
 
     def __init__(self, debug=False):
         self.debug = debug
 
     @staticmethod
-    def create_for_shell_script(shell_script, command, debug):
-        '''Factory for using as a drop-in replacement to a shell script.
+    def create_runner(runner_name, command, debug):
+        for cls in ZephyrBinaryRunner.__subclasses__():
+            if cls.name() == runner_name:
+                break
+        else:
+            raise ValueError('no runner named {} is known'.format(runner_name))
 
-        Command is one of 'flash', 'debug', 'debugserver'.
+        if not cls.handles_command(command):
+            raise ValueError('runner {} does not implement command {}'.format(
+                runner_name, command))
 
-        Get runner instance to use in place of shell_script, deriving
-        configuration from the environment.'''
-        for sub_cls in ZephyrBinaryRunner.__subclasses__():
-            if sub_cls.replaces_shell_script(shell_script, command):
-                return sub_cls.create_from_env(command, debug)
-        raise ValueError('cannot implement script {} command {}'.format(
-                             shell_script, command))
+        return cls.create_from_env(command, debug)
 
-    @staticmethod
+    @classmethod
     @abc.abstractmethod
-    def replaces_shell_script(shell_script, command):
-        '''Check if this class replaces shell_script for the given command.'''
+    def name(cls):
+        '''Return this runner's user-visible name.
+
+        When choosing a name, pick something short and lowercase,
+        based on the name of the tool (like openocd, jlink, etc.) or
+        the target architecture/board (like xtensa, em-starterkit,
+        etc.).'''
+
+    @classmethod
+    def handles_command(cls, command):
+        '''Return True iff this class can run the given command.
+
+        The default implementation returns True if the command is
+        valid (i.e. is one of "flash", "debug", and "debugserver").
+        Subclasses should override if they only provide a subset.'''
+        return command in {'flash', 'debug', 'debugserver'}
 
     @staticmethod
     @abc.abstractmethod
     def create_from_env(command, debug):
-        '''Create new flasher instance from environment variables.
-
-        This class must be able to replace the current shell script
-        (FLASH_SCRIPT or DEBUG_SCRIPT, depending on command). The
-        environment variables expected by that script are used to build
-        the flasher in a backwards-compatible manner.'''
+        '''Create new runner instance from environment variables.'''
 
     def run(self, command, **kwargs):
         '''Runs command ('flash', 'debug', 'debugserver').
 
         This is the main entry point to this runner.'''
+        if not self.handles_command(command):
+            raise ValueError('runner {} does not implement command {}'.format(
+                self.name(), command))
         self.do_run(command, **kwargs)
 
     @abc.abstractmethod
diff --git a/scripts/support/runner/dfu.py b/scripts/support/runner/dfu.py
index 95353b9..36399f9 100644
--- a/scripts/support/runner/dfu.py
+++ b/scripts/support/runner/dfu.py
@@ -25,8 +25,13 @@
         except ValueError:
             self.list_pattern = ', name="{}",'.format(self.alt)
 
-    def replaces_shell_script(shell_script, command):
-        return command == 'flash' and shell_script == 'dfuutil.sh'
+    @classmethod
+    def name(cls):
+        return 'dfu-util'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command == 'flash'
 
     def create_from_env(command, debug):
         '''Create flasher from environment.
@@ -59,9 +64,6 @@
         return self.list_pattern in output
 
     def do_run(self, command, **kwargs):
-        if command != 'flash':
-            raise ValueError('only flash is supported')
-
         reset = 0
         if not self.find_device():
             reset = 1
diff --git a/scripts/support/runner/esp32.py b/scripts/support/runner/esp32.py
index 38f6fbe..e666f9b 100644
--- a/scripts/support/runner/esp32.py
+++ b/scripts/support/runner/esp32.py
@@ -25,8 +25,13 @@
         self.flash_mode = flash_mode
         self.espidf = espidf
 
-    def replaces_shell_script(shell_script, command):
-        return command == 'flash' and shell_script == 'esp32.sh'
+    @classmethod
+    def name(cls):
+        return 'esp32'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command == 'flash'
 
     def create_from_env(command, debug):
         '''Create flasher from environment.
@@ -68,9 +73,6 @@
                                  debug=debug)
 
     def do_run(self, command, **kwargs):
-        if command != 'flash':
-            raise ValueError('only flash is supported')
-
         bin_name = path.splitext(self.elf)[0] + path.extsep + 'bin'
         cmd_convert = [self.espidf, '--chip', 'esp32', 'elf2image', self.elf]
         cmd_flash = [self.espidf, '--chip', 'esp32', '--port', self.device,
diff --git a/scripts/support/runner/jlink.py b/scripts/support/runner/jlink.py
index 3822173..2ad673e 100644
--- a/scripts/support/runner/jlink.py
+++ b/scripts/support/runner/jlink.py
@@ -28,9 +28,13 @@
         self.gdb_port = gdb_port
         self.tui_arg = [tui] if tui is not None else []
 
-    def replaces_shell_script(shell_script, command):
-        return (command in {'debug', 'debugserver'} and
-                shell_script == 'jlink.sh')
+    @classmethod
+    def name(cls):
+        return 'jlink'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command in {'debug', 'debugserver'}
 
     def create_from_env(command, debug):
         '''Create runner from environment.
@@ -80,9 +84,6 @@
         print('JLink GDB server running on port {}'.format(self.gdb_port))
 
     def do_run(self, command, **kwargs):
-        if command not in {'debug', 'debugserver'}:
-            raise ValueError('{} is not supported'.format(command))
-
         server_cmd = (self.gdbserver_cmd +
                       ['-port', str(self.gdb_port),
                        '-if', self.iface,
diff --git a/scripts/support/runner/nios2.py b/scripts/support/runner/nios2.py
index a18d2e3..2916360 100644
--- a/scripts/support/runner/nios2.py
+++ b/scripts/support/runner/nios2.py
@@ -29,9 +29,9 @@
         self.gdb_cmd = [gdb] if gdb is not None else None
         self.tui_arg = [tui] if tui is not None else []
 
-    def replaces_shell_script(shell_script, command):
-        return (command in {'flash', 'debug', 'debugserver'} and
-                shell_script == 'nios2.sh')
+    @classmethod
+    def name(cls):
+        return 'nios2'
 
     def create_from_env(command, debug):
         '''Create runner from environment.
@@ -77,9 +77,6 @@
                                  gdb=gdb, tui=tui, debug=debug)
 
     def do_run(self, command, **kwargs):
-        if command not in {'flash', 'debug', 'debugserver'}:
-            raise ValueError('{} is not supported'.format(command))
-
         if command == 'flash':
             self.flash(**kwargs)
         else:
diff --git a/scripts/support/runner/nrfjprog.py b/scripts/support/runner/nrfjprog.py
index 0ebed7b..b52eba8 100644
--- a/scripts/support/runner/nrfjprog.py
+++ b/scripts/support/runner/nrfjprog.py
@@ -18,8 +18,13 @@
         self.hex_ = hex_
         self.family = family
 
-    def replaces_shell_script(shell_script, command):
-        return command == 'flash' and shell_script == 'nrf_flash.sh'
+    @classmethod
+    def name(cls):
+        return 'nrfjprog'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command == 'flash'
 
     def create_from_env(command, debug):
         '''Create flasher from environment.
@@ -61,9 +66,6 @@
         return snrs[value - 1]
 
     def do_run(self, command, **kwargs):
-        if command != 'flash':
-            raise ValueError('only flash is supported')
-
         board_snr = self.get_board_snr_from_user()
 
         print('Flashing file: {}'.format(self.hex_))
diff --git a/scripts/support/runner/openocd.py b/scripts/support/runner/openocd.py
index 23b727e..85dcaf1 100644
--- a/scripts/support/runner/openocd.py
+++ b/scripts/support/runner/openocd.py
@@ -47,9 +47,9 @@
         self.gdb_cmd = [gdb] if gdb is not None else None
         self.tui_arg = [tui] if tui is not None else []
 
-    def replaces_shell_script(shell_script, command):
-        return (command in {'flash', 'debug', 'debugserver'} and
-                shell_script == 'openocd.sh')
+    @classmethod
+    def name(cls):
+        return 'openocd'
 
     def create_from_env(command, debug):
         '''Create runner from environment.
@@ -135,9 +135,6 @@
                                    gdb=gdb, tui=tui, debug=debug)
 
     def do_run(self, command, **kwargs):
-        if command not in {'flash', 'debug', 'debugserver'}:
-            raise ValueError('{} is not supported'.format(command))
-
         if command == 'flash':
             self.do_flash(**kwargs)
         elif command == 'debug':
diff --git a/scripts/support/runner/pyocd.py b/scripts/support/runner/pyocd.py
index d5e7831..2e98f1c 100644
--- a/scripts/support/runner/pyocd.py
+++ b/scripts/support/runner/pyocd.py
@@ -41,9 +41,9 @@
             daparg_args = ['-da', daparg]
         self.daparg_args = daparg_args
 
-    def replaces_shell_script(shell_script, command):
-        return (command in {'flash', 'debug', 'debugserver'} and
-                shell_script == 'pyocd.sh')
+    @classmethod
+    def name(cls):
+        return 'pyocd'
 
     def port_args(self):
         return ['-p', str(self.gdb_port)]
@@ -108,9 +108,6 @@
                                  board_id=board_id, daparg=daparg, debug=debug)
 
     def do_run(self, command, **kwargs):
-        if command not in {'flash', 'debug', 'debugserver'}:
-            raise ValueError('{} is not supported'.format(command))
-
         if command == 'flash':
             self.flash(**kwargs)
         else:
diff --git a/scripts/support/runner/qemu.py b/scripts/support/runner/qemu.py
index 4852268..8bea47e 100644
--- a/scripts/support/runner/qemu.py
+++ b/scripts/support/runner/qemu.py
@@ -13,8 +13,9 @@
     def __init__(self, debug=False):
         super(QemuBinaryRunner, self).__init__(debug=debug)
 
-    def replaces_shell_script(shell_script, command):
-        return shell_script == 'qemu.sh'
+    @classmethod
+    def name(cls):
+        return 'qemu'
 
     def create_from_env(command, debug):
         '''Create runner. No environment dependencies.'''
diff --git a/scripts/support/runner/xtensa.py b/scripts/support/runner/xtensa.py
index 35d3057..a73ba36 100644
--- a/scripts/support/runner/xtensa.py
+++ b/scripts/support/runner/xtensa.py
@@ -17,8 +17,13 @@
         self.gdb_cmd = [gdb]
         self.elf_name = elf_name
 
-    def replaces_shell_script(shell_script, command):
-        return command == 'debug' and shell_script == 'xt-gdb.sh'
+    @classmethod
+    def name(cls):
+        return 'xtensa'
+
+    @classmethod
+    def handles_command(cls, command):
+        return command == 'debug'
 
     def create_from_env(command, debug):
         '''Create runner from environment.
@@ -36,9 +41,6 @@
         return XtensaBinaryRunner(xt_gdb, elf_name)
 
     def do_run(self, command, **kwargs):
-        if command != 'debug':
-            raise ValueError('Only debug is supported')
-
         gdb_cmd = (self.gdb_cmd + [self.elf_name])
 
         self.check_call(gdb_cmd)
diff --git a/scripts/support/zephyr_flash_debug.py b/scripts/support/zephyr_flash_debug.py
index ebac278..a600465 100755
--- a/scripts/support/zephyr_flash_debug.py
+++ b/scripts/support/zephyr_flash_debug.py
@@ -27,13 +27,12 @@
 #   python zephyr_flash_debug.py openocd --openocd-bin=/openocd/path ...
 #
 # For now, maintain compatibility.
-def run(shell_script, command, debug):
+def run(runner_name, command, debug):
     try:
-        runner = ZephyrBinaryRunner.create_for_shell_script(shell_script,
-                                                            command,
-                                                            debug)
+        runner = ZephyrBinaryRunner.create_runner(runner_name, command, debug)
     except ValueError:
-        print('Unrecognized shell script {}'.format(shell_script),
+        print('runner {} is not available or does not support {}'.format(
+                  runner_name, command),
               file=sys.stderr)
         raise
 
@@ -45,10 +44,10 @@
     debug = True
     try:
         debug = get_env_bool_or('VERBOSE', False)
-        if len(sys.argv) != 3 or sys.argv[1] not in commands:
-            raise ValueError('usage: {} <{}> script-name'.format(
+        if len(sys.argv) != 3 or sys.argv[2] not in commands:
+            raise ValueError('usage: {} <runner-name> <{}>'.format(
                 sys.argv[0], '|'.join(commands)))
-        run(sys.argv[2], sys.argv[1], debug)
+        run(sys.argv[1], sys.argv[2], debug)
     except Exception as e:
         if debug:
             raise