#!/usr/bin/env bash # kaleidoscope-builder - Kaleidoscope helper tool # Copyright (C) 2017-2018 Keyboard.io, Inc. # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation, version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . set -e ###### ###### Build and output configuration ###### identify_os() { ## Platform-specific overrides # Shamelessly stolen from git's Makefile uname_S=$(uname -s 2>/dev/null || echo not) uname_O=$(uname -o 2>/dev/null || echo not) } absolute_filename() { echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" } read_conf_files() { for conf_file in \ "${HOME}/.kaleidoscope-builder.conf" \ "$(pwd)/.kaleidoscope-builder.conf" \ "$(pwd)/kaleidoscope-builder.conf" \ "${KALEIDOSCOPE_DIR}/etc/kaleidoscope-builder.conf"; do if [ -e "${conf_file}" ]; then # shellcheck disable=SC1090 . "${conf_file}" fi done } configure_arduino_cli_env() { SYSTEM_ARDUINO_CLI="$(command -v arduino-cli || true)" if [ -z "${SYSTEM_ARDUINO_CLI}" ]; then : "${ARDUINO_CLI:=${KALEIDOSCOPE_BIN_DIR}/arduino-cli}" else : "${ARDUINO_CLI:=${SYSTEM_ARDUINO_CLI}}" fi : "${ARDUINO_CONTENT:=${KALEIDOSCOPE_DIR}/.arduino}" : "${ARDUINO_DIRECTORIES_DATA:=${ARDUINO_CONTENT}/data}" : "${ARDUINO_DIRECTORIES_DOWNLOADS:=${ARDUINO_CONTENT}/downloads}" : "${ARDUINO_DIRECTORIES_USER:=${ARDUINO_CONTENT}/user}" : "${ARDUINO_CLI_CONFIG:=${ARDUINO_DIRECTORIES_DATA}/arduino-cli.yaml}" : "${ARDUINO_BOARDS_MANAGER_KALEIDOSCOPE:=https://raw.githubusercontent.com/keyboardio/boardsmanager/master/package_keyboardio_index.json}" } install_arduino_cli() { # todo cd to kaleidoscope dir curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh } configure_arduino_cli() { if [ -z "${ARDUINO_CLI}" ]; then install_arduino_cli fi if [ -z "${ARDUINO_CLI_CONFIG}" ]; then install_arduino_cli_config fi } install_arduino_cli_config() { run_arduino_cli config init } install_arduino_core_kaleidoscope() { install_arduino_core_avr install_arduino_core "keyboardio:avr" } install_arduino_core_avr() { install_arduino_core "arduino:avr" } install_arduino_core() { run_arduino_cli core install "$1" } run_arduino_cli() { ARDUINO_DIRECTORIES_USER=${ARDUINO_DIRECTORIES_USER} \ ARDUINO_DIRECTORIES_DATA=${ARDUINO_DIRECTORIES_DATA} \ ARDUINO_DIRECTORIES_DOWNLOADS=${ARDUINO_DIRECTORIES_DOWNLOADS} \ ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS=${ARDUINO_BOARDS_MANAGER_KALEIDOSCOPE} \ "${ARDUINO_CLI}" "$@" } build_version() { : "${LIB_PROPERTIES_PATH:="../.."}" GIT_VERSION="$( cd "${SKETCH_DIR}" if [ -d .git ]; then echo -n '-g' && git describe --abbrev=4 --dirty --always; fi )" LIB_VERSION="$( cd "${SKETCH_DIR}" (grep version= "${LIB_PROPERTIES_PATH}/library.properties" 2>/dev/null || echo version=0.0.0) | cut -d= -f2 )${GIT_VERSION}" } build_paths() { # We need that echo because we\re piping to cksum # shellcheck disable=SC2005 SKETCH_IDENTIFIER="$(echo "${SKETCH_FILE_PATH}" | cksum | cut -d ' ' -f 1)-${SKETCH_FILE_NAME}" : "${KALEIDOSCOPE_TEMP_PATH:=${TMPDIR:-/tmp}/kaleidoscope-${USER}}" : "${KALEIDOSCOPE_BUILD_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}" : "${KALEIDOSCOPE_OUTPUT_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}" : "${SKETCH_OUTPUT_DIR:=${SKETCH_IDENTIFIER}/output}" : "${SKETCH_BUILD_DIR:=${SKETCH_IDENTIFIER}/build}" : "${BUILD_PATH:=${KALEIDOSCOPE_BUILD_PATH}/${SKETCH_BUILD_DIR}}" : "${OUTPUT_PATH:=${KALEIDOSCOPE_OUTPUT_PATH}/${SKETCH_OUTPUT_DIR}}" : "${CCACHE_WRAPPER_PATH:=${KALEIDOSCOPE_TEMP_PATH}/ccache/bin}" : "${CORE_CACHE_PATH:=${KALEIDOSCOPE_TEMP_PATH}/arduino-cores}" mkdir -p "$CORE_CACHE_PATH" mkdir -p "$BUILD_PATH" } build_filenames() { : "${OUTPUT_FILE_PREFIX:=${SKETCH_BASE_NAME}-${LIB_VERSION}}" : "${HEX_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.hex}" : "${HEX_FILE_WITH_BOOTLOADER_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}-with-bootloader.hex}" : "${ELF_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.elf}" : "${LIB_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.a}" } enable_ccache() { if [ -z "${CCACHE_NOT_SUPPORTED}" ] && [ "$(command -v ccache)" ]; then if ! [ -d "$CCACHE_WRAPPER_PATH" ]; then mkdir -p "$CCACHE_WRAPPER_PATH" if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}" ]; then ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}" fi if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}" ]; then ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}" fi if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm" ]; then ln -s "${AVR_NM}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm" fi if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy" ]; then ln -s "${AVR_OBJCOPY}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy" fi if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar" ]; then ln -s "${AVR_AR}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar" fi if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size" ]; then ln -s "${AVR_SIZE}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size" fi fi export CCACHE_PATH=${COMPILER_PATH}/ CCACHE_ENABLED=1 fi } firmware_size() { if [ "${ARCH}" = "virtual" ]; then echo "[Size not computed for virtual build]" return fi : "${MAX_PROG_SIZE:=$(get_arduino_pref 'upload.maximum_size')}" ## This is a terrible hack, please don't hurt me. - algernon set +e raw_output=$("$@" 2>/dev/null) rc=$? set -e if [ $rc -eq 0 ]; then output="$(echo "${raw_output}" | grep "\\(Program\\|Data\\):" | sed -e 's,^, - ,' && echo)" PROGSIZE="$(echo "${output}" | grep "Program:" | cut -d: -f2 | awk '{print $1}')" PERCENT="$(echo "${PROGSIZE}" "${MAX_PROG_SIZE}" | awk "{ printf \"%02.01f\", \$1 / \$2 * 100 }")" # we want the sed there, doing with shell builtins would be worse. # shellcheck disable=SC2001 disable=SC1117 echo "${output}" | sed -e "s/\(Program:.*\)(\([0-9\.]*%\) Full)/\1(${PERCENT}% Full)/" else echo "Unable to determine image size." fi } find_sketch() { SKETCH_DIR="${SKETCH:-$(absolute_filename "$(pwd)")}" SKETCH_BASE_NAME=$(basename "${SKETCH_DIR}") SKETCH_FILE_NAME="${SKETCH_BASE_NAME}.ino" # Arduino sketches are usually a file inside directory Foo named Foo.ino, so try that as a fallback for dir in \ "${SKETCH_DIR}" \ "src/" \ "."; do if [ -f "${dir}/${SKETCH_FILE_NAME}" ]; then SKETCH_DIR="${dir}" SKETCH_FILE_PATH=$(absolute_filename "${dir}/${SKETCH_FILE_NAME}") return fi done echo "I couldn't find your sketch (.ino file)" >&2 exit 1 } find_device_vid_pid() { : "${VID:=$(get_arduino_pref 'build.vid')}" : "${SKETCH_PID:=$(get_arduino_pref 'build.pid')}" : "${BOOTLOADER_PID:=$(get_arduino_pref 'bootloader.pid')}" : "${BOOTLOADER_VID:=$(get_arduino_pref 'bootloader.vid')}" } prompt_before_flashing() { flashing_instructions=$(get_arduino_pref 'build.flashing_instructions') if [ "x${flashing_instructions}x" = "xx" ]; then flashing_instructions="If your keyboard needs you to do something to put it in flashing mode, do that now." fi printf '%b\n\n' "${flashing_instructions}" echo "" echo "When you're ready to proceed, press 'Enter'." # We do not want to permit line continuations here. We just want a newline. # shellcheck disable=SC2162 read } reset_device_cmd() { if [ -z ${NO_RESET} ]; then if [ "${uname_S}" = "Darwin" ]; then /bin/stty -f ${DEVICE_PORT} 1200 elif [ "${uname_S}" = "FreeBSD" ]; then /bin/stty -f ${DEVICE_PORT} 1200 elif [ "${uname_O}" = "Cygwin" ]; then cmd /c mode ${DEVICE_COM_PORT} baud=1200 else stty -F ${DEVICE_PORT} 1200 hupcl fi fi } flash() { compile "$@" # Check to see if we can see a keyboard bootloader port. # If we -can-, then we should skip over the "reset to bootloader" thing find_bootloader_ports if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then prompt_before_flashing # This is defined in the (optional) user config. # shellcheck disable=SC2154 ${preFlash_HOOKS} # If we're -not- doing a manual reset, then try to do it automatically if [ -z "${MANUAL_RESET}" ]; then reset_device sleep 2 find_bootloader_ports # Otherwise, poll for a bootloader port. else wait_for_bootloader_port fi fi check_bootloader_port_and_flash # This is defined in the (optional) user config. # shellcheck disable=SC2154 ${postFlash_HOOKS} } wait_for_bootloader_port() { declare -i tries tries=15 while [ "$tries" -gt 0 ] && [ -z "${DEVICE_PORT_BOOTLOADER}" ]; do sleep 1 printf "." find_bootloader_ports # the variable annotations do appear to be necessary # shellcheck disable=SC2004 tries=$(($tries - 1)) done if [ "$tries" -gt 0 ]; then echo "Found." else echo "Timed out." fi } check_bootloader_port() { if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then echo "Unable to detect a keyboard in bootloader mode." echo "You may need to hold a key or hit a reset button." echo "Please check your keyboard's documentation" return 1 fi } check_bootloader_port_and_flash() { if ! check_bootloader_port; then return 1 fi echo "Flashing your keyboard:" # If the flash fails, try a second time if ! flash_over_usb; then sleep 2 if ! flash_over_usb; then if [ "${ARDUINO_VERBOSE}" != "--verbose" ]; then echo "Something went wrong." echo "You might want to try flashing again with the VERBOSE environment variable set" fi return 1 fi fi echo "Keyboard flashed successfully!" return 0 } flash_from_bootloader() { compile "$@" prompt_before_flashing find_bootloader_ports check_bootloader_port_and_flash } program() { compile "$@" prompt_before_flashing flash_with_programmer } flash_with_programmer() { ${AVRDUDE_CMD} \ -cusbtiny \ -B 1 \ "-Uflash:w:${HEX_FILE_PATH}:i" } flash_over_usb() { FLASH_CMD=$(${AVRDUDE_CMD} \ -cavr109 \ -P "${DEVICE_PORT_BOOTLOADER}" \ -b57600 \ "-Uflash:w:${HEX_FILE_PATH}:i") if [ "${ARDUINO_VERBOSE}" != "--verbose" ]; then ${FLASH_CMD} 2>&1 | grep -v ^avrdude | grep -v '^$' | grep -v '^ ' | grep -vi programmer return "${PIPESTATUS[0]}" else ${FLASH_CMD} return $? fi } hex_with_bootloader() { compile : "${BOOTLOADER_PATH:=$(get_arduino_pref 'runtime.platform.path')/bootloaders/$(get_arduino_pref 'bootloader.file')}" awk '/^:00000001FF/ == 0' "${HEX_FILE_PATH}" >"${HEX_FILE_WITH_BOOTLOADER_PATH}" echo "Using ${BOOTLOADER_PATH}" cat "${BOOTLOADER_PATH}" >>"${HEX_FILE_WITH_BOOTLOADER_PATH}" ln -sf -- "${OUTPUT_FILE_PREFIX}-with-bootloader.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest-with-bootloader.hex" cat <<-EOF Combined firmware and bootloader are now at ${HEX_FILE_WITH_BOOTLOADER_PATH} Make sure you have the bootloader version you expect. And TEST THIS ON REAL HARDWARE BEFORE YOU GIVE IT TO ANYONE EOF } build() { compile "$@" size "$@" } prepare_ccache() { find_sketch build_paths enable_ccache } compile() { find_sketch set_executable_paths build_version build_paths build_filenames # If the hex file is older than the sketch file, or the hex file does not exist # then rebuild. This is not as correct as letting make check our dependencies # But it's less broken for most user use cases # TODO(anyone): Make this suck less if [ "${HEX_FILE_PATH}" -ot "${SKETCH_FILE_PATH}" ]; then do_compile "$@" else echo "${HEX_FILE_PATH} did not need to be rebuilt" fi } set_executable_paths() { ###### ###### Executable paths ###### # Allow the compiler path to be empty for virtual builds # should use compiler.path instead of appending bin, but we don't have substitution het : "${COMPILER_PATH=$(get_arduino_pref 'runtime.tools.avr-gcc.path')/bin}" COMPILER_SUFFIX="" C_COMPILER_BASENAME=$(basename "${CC:-gcc}") CXX_COMPILER_BASENAME=$(basename "${CXX:-g++}") AR_BASENAME=$(basename "${AR:-ar}") # Allow the compiler prefix to be empty for virtual builds COMPILER_PREFIX="${COMPILER_PREFIX-avr-}" : "${AVR_SIZE:=${COMPILER_PATH}/${COMPILER_PREFIX}size}" : "${AVR_OBJDUMP:=${COMPILER_PATH}/${COMPILER_PREFIX}objdump}" : "${AVR_OBJCOPY:=${COMPILER_PATH}/${COMPILER_PREFIX}objcopy}" : "${AVR_NM:=${COMPILER_PATH}/${COMPILER_PREFIX}nm}" : "${AVR_AR:=${COMPILER_PATH}/${COMPILER_PREFIX}ar}" # TO DO should use tools.avrdude.cmd.path and tools.avrdude.config.path instead of hardcoding : "${AVRDUDE:=$(get_arduino_pref 'runtime.tools.avrdude.path')/bin/avrdude}" : "${AVRDUDE_CONF:=$(get_arduino_pref 'runtime.tools.avrdude.path')/etc/avrdude.conf}" AVRDUDE_CMD="${AVRDUDE} -v-C \"${AVRDUDE_CONF}\" -D -p\"$(get_arduino_pref 'build.mcu')\"" } do_compile() { prepare_ccache install -d "${OUTPUT_PATH}" echo "Building ${SKETCH_FILE_PATH}" # This is defined in the (optional) user config. # shellcheck disable=SC2154 ${compile_HOOKS} SAVED_BOARD="${BOARD}" SAVED_FQBN="${FQBN}" if [ -e "${SKETCH_DIR}/.kaleidoscope-builder.conf" ]; then # shellcheck disable=SC1090 BOARD="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${BOARD}")" # shellcheck disable=SC1090 FQBN="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${FQBN}")" if [ -n "${BOARD}" ]; then : "${ARCH:=avr}" FQBN="keyboardio:${ARCH}:${BOARD}" fi fi do_compile_with_cli if [ -z "${LIBONLY}" ]; then cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.hex" "${HEX_FILE_PATH}" cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.elf" "${ELF_FILE_PATH}" ln -sf "${OUTPUT_FILE_PREFIX}.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.hex" ln -sf "${OUTPUT_FILE_PREFIX}.elf" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.elf" else cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.a" "${LIB_FILE_PATH}" ln -sf "${OUTPUT_FILE_PREFIX}.a" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.a" fi if [ "${ARDUINO_VERBOSE}" == "--verbose" ]; then echo "Build artifacts can be found in ${BUILD_PATH}" fi BOARD="${SAVED_BOARD}" FQBN="${SAVED_FQBN}" } get_arduino_pref() { pref=$1 # Strip the preference name. And then strip leading and trailing quotations MESSAGE=$(dump_arduino_prefs | grep --max-count=1 "${pref}=" | sed -e s/^.*"${pref}"=// -e 's/^"//' -e 's/"$//') echo "$MESSAGE" } dump_arduino_prefs() { if [ "x${_ARDUINO_PREFS}x" == "xx" ]; then _ARDUINO_PREFS=$(run_arduino_cli --fqbn "${FQBN}" compile --show-properties "${SKETCH_FILE_PATH}") fi echo "$_ARDUINO_PREFS" } do_compile_with_cli() { #-build-cache "${CORE_CACHE_PATH}" \ if [ $CCACHE_ENABLED ]; then COMPILER_PATH_PROP="${CCACHE_WRAPPER_PATH}/" else COMPILER_PATH_PROP="${COMPILER_PATH}" fi _CMD_CXX="${CXX:-${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}${COMPILER_SUFFIX}}" _CMD_CC="${CC:-${COMPILER_PREFIX}${C_COMPILER_BASENAME}${COMPILER_SUFFIX}}" _CMD_AR="${AR:-${COMPILER_PREFIX}${AR_BASENAME}${COMPILER_SUFFIX}}" run_arduino_cli compile \ --fqbn "${FQBN}" \ --libraries "${KALEIDOSCOPE_DIR}/.." \ --build-path "${BUILD_PATH}" \ --output-dir "${OUTPUT_PATH}" \ --build-properties "compiler.path=${COMPILER_PATH_PROP}" \ --build-properties "compiler.c.cmd=${_CMD_CC}" \ --build-properties "compiler.cpp.cmd=${_CMD_CXX}" \ --build-properties "compiler.ar.cmd=${_CMD_AR}" \ --build-properties "compiler.c.elf.cmd=${_CMD_CXX}" \ --build-properties "compiler.cpp.extra_flags=${LOCAL_CFLAGS}" \ --warnings all \ "${SKETCH_FILE_PATH}" } find_all_sketches() { for sketch_name in ./*.ino \ $([ -d examples ] && find examples -name '*.ino') \ src/*.ino; do if [ -d "$(dirname "${sketch_name}")" ] || [ -f "${sketch_name}" ]; then p="$(basename "${sketch_name}" .ino)" if [ "${p}" != '*' ]; then case "${sketch_name}" in examples/*/${p}/${p}.ino) echo "${sketch_name}" | sed -e "s,examples/,," | sed -e "s,/${p}\\.ino,," ;; *) echo "${p}" ;; esac fi fi done | sort } build_all() { plugins="$(find_all_sketches)" for sketch in ${plugins}; do export SKETCH="${sketch}" $0 "${sketch}" build done } size() { compile : "${AVR_SIZE_FLAGS:=-C --mcu=$(get_arduino_pref 'build.mcu')}" echo "- Size: ${ELF_FILE_PATH}" # shellcheck disable=SC2086 firmware_size "${AVR_SIZE}" ${AVR_SIZE_FLAGS} "${ELF_FILE_PATH}" echo } size_map() { compile "${AVR_NM}" --size-sort -C -r -l -t decimal "${ELF_FILE_PATH}" } disassemble() { compile "${AVR_OBJDUMP}" -C -d "${ELF_FILE_PATH}" } decompile() { disassemble } clean() { find_sketch build_paths if [ -d "$OUTPUT_PATH" ]; then rm -rf -- "${OUTPUT_PATH}" fi } reset_device() { find_device_port check_device_port reset_device_cmd } find_bootloader_ports() { if [ -n "${DEVICE_PORT_BOOTLOADER}" ]; then echo "DEVICE_PORT_BOOTLOADER=\"${DEVICE_PORT_BOOTLOADER}\" predefined." return fi find_device_vid_pid : "${BOOTLOADER_VID:=${VID}}" if [ "${uname_S}" = "Darwin" ]; then DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos" DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})" elif [ "${uname_O}" = "Cygwin" ]; then DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1" DEVICE_PORT_BOOTLOADER="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID} -Format COM)" elif [ "${uname_S}" = "FreeBSD" ]; then DEVICE_PORT_PROBER="${KALEIDOSCPE_BIN_DIR}/find-device-port-freebsd" DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER})" else DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev" DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})" fi } find_device_port() { if [ -n "${DEVICE_PORT}" ]; then echo "DEVICE_PORT=\"${DEVICE_PORT}\" predefined." return fi find_device_vid_pid if [ "${uname_S}" = "Darwin" ]; then DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos" DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})" elif [ "${uname_O}" = "Cygwin" ]; then DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1" DEVICE_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format Cygwin)" DEVICE_COM_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format COM)" elif [ "${uname_S}" = "FreeBSD" ]; then DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-freebsd" DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER})" else DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev" DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})" fi } check_device_port() { if [ -z "$DEVICE_PORT" ]; then cat <&2 I couldn't autodetect the keyboard's serial port. If you see this message and your keyboard is connected to your computer, it may mean that our serial port detection logic is buggy or incomplete. In that case, please report this issue at: https://github.com/keyboardio/Kaleidoscope EOF exit 1 elif echo "$DEVICE_PORT" | grep -q '[[:space:]]'; then cat <&2 Unexpected whitespace found in detected serial port: $DEVICE_PORT If you see this message, it means that our serial port detection logic is buggy or incomplete. Please report this issue at: https://github.com/keyboardio/Kaleidoscope EOF exit 1 fi if ! [ -w "$DEVICE_PORT" ]; then cat <&2 In order to update your keyboard's firmware you need to have permission to write to its serial port $DEVICE_PORT. It appears that you do not have this permission: $(ls -l "$DEVICE_PORT") This may be because you're not in the correct unix group: $(stat -c %G "$DEVICE_PORT"). You are currently in the following groups: $(id -Gn) Please ensure you have followed the instructions on setting up your account to be in the right group: https://github.com/keyboardio/Kaleidoscope/wiki/Install-Arduino-support-on-Linux EOF exit 1 fi } usage() { cat <<-EOF Usage: $0 SKETCH commands... Runs all of the commands in the context of the Sketch. Available commands: help This help screen. compile Compiles the sketch. size Reports the size of the compiled sketch. build Runs compile and report-size. clean Cleans up the output directory. size-map Displays the size map for the sketch. disassemble Decompile the sketch. reset-device Reset the device. flash Flashes the firmware using avrdude. EOF } help() { usage } if [ $# -lt 1 ]; then usage exit 1 fi # Temporary migration for old makefiles # I'm not thrilled about how we default to system Arduino dirs. if [ -n "${BOARD_HARDWARE_PATH}" ]; then ARDUINO_DIRECTORIES_USER="${BOARD_HARDWARE_PATH}/../" fi : "${KALEIDOSCOPE_DIR:=$( cd "$(dirname "$0")"/.. pwd )}" # shellcheck disable=SC2034 : "${KALEIDOSCOPE_BIN_DIR:=${KALEIDOSCOPE_DIR}/bin/}" identify_os read_conf_files configure_arduino_cli_env # shellcheck disable=SC1090 if [ -n "${VERBOSE}" ] && [[ "${VERBOSE}" -gt 0 ]]; then ARDUINO_VERBOSE="--verbose" else ARDUINO_VERBOSE="--quiet" fi ## Parse the command-line ## - anything that has a =, is an env var ## - from the remaining stuff, the first one is the Library/Sketch ## - everything else are commands ## ## - if there is only one argument, that's a command cmds="" ## Export vars for i in $(seq 1 $#); do v="$1" shift case "${v}" in *=*) # Exporting an expansion is *precisely* what we want here. # shellcheck disable=SC2086,SC2163 export ${v} ;; *) cmds="${cmds} ${v}" ;; esac done # Word splitting is desired here. # shellcheck disable=SC2086 set -- ${cmds} if [ $# -eq 1 ]; then cmd="$(echo "$1" | tr '-' '_')" ${cmd} exit $? fi SKETCH="$1" shift cmds="" # shellcheck disable=2034 for i in $(seq 1 $#); do cmds="${cmds} $(echo "$1" | tr '-' '_')" shift done for cmd in ${cmds}; do ${cmd} done