#!/usr/bin/env bash set -e ###### ###### Build and output configuration ###### build_version () { GIT_VERSION="$(cd "$(find_sketch)"; git describe --abbrev=4 --dirty --always)" LIB_VERSION="$(cd "$(find_sketch)"; (grep version= ../../library.properties 2>/dev/null || echo version=0.0.0) | cut -d= -f2)-g${GIT_VERSION}" BUILD_PATH="${BUILD_PATH:-"$(mktemp -d 2>/dev/null || mktemp -d -t 'build')"}" OUTPUT_DIR="${OUTPUT_DIR:-output/${LIBRARY}}" OUTPUT_PATH="${OUTPUT_PATH:-${SOURCEDIR}/${OUTPUT_DIR}}" } build_filenames () { OUTPUT_FILE_PREFIX="${SKETCH}-${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" } firmware_size () { if [ "${BOARD}" = "virtual" ]; then echo "[Size not computed for virtual build]" return fi ## This is a terrible hack, please don't hurt me. - algernon MAX_PROG_SIZE=28672 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 }")" echo "${output}" | sed -e "s/\(Program:.*\)(\([0-9\.]*%\) Full)/\1(${PERCENT}% Full)/" } find_sketch () { SKETCH="${SKETCH:-${DEFAULT_SKETCH}}" LIBRARY="${LIBRARY:-${SKETCH}}" if [ -z "${SKETCH}" ] || [ -z "${LIBRARY}" ] || [ -z "${ROOT}" ] || [ -z "${SOURCEDIR}" ]; then echo "SKETCH, LIBRARY, SOURCEDIR, and ROOT need to be set before including this file!" >&2 exit 1 fi for path in "examples/${LIBRARY}" \ "src" \ "."; do if [ -f "${path}/${SKETCH}.ino" ]; then echo "${path}" return fi done echo "Couldn't find sketch (.ino file)" >&2 exit 1 } prepare_to_flash () { if [ ! -e "${HEX_FILE_PATH}" ]; then compile size fi echo "Press ENTER when ready..." read } flash () { prepare_to_flash reset_device sleep 3s find_bootloader_ports flash_over_usb || flash_over_usb } flash_over_usb () { if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then echo 'DEVICE_PORT_BOOTLOADER is empty (did you hold "Prog" to enable bootloader mode?)' >&2 return 1 fi sleep 1s ${AVRDUDE} -q -q -C ${AVRDUDE_CONF} -p${MCU} -cavr109 -D -P ${DEVICE_PORT_BOOTLOADER} -b57600 "-Uflash:w:${HEX_FILE_PATH}:i" } flash_from_bootloader() { prepare_to_flash find_bootloader_ports flash_over_usb || flash_over_usb } program() { prepare_to_flash flash_with_programmer } flash_with_programmer() { ${AVRDUDE} -v \ -C ${AVRDUDE_CONF} \ -p${MCU} \ -cusbtiny \ -D \ -B 1 \ "-Uflash:w:${HEX_FILE_PATH}:i" } hex_with_bootloader () { if [ ! -e "${HEX_FILE_PATH}" ]; then compile fi awk '/^:00000001FF/ == 0' "${HEX_FILE_PATH}" > "${HEX_FILE_WITH_BOOTLOADER_PATH}" echo "Using ${BOOTLOADER_PATH}" ${MD5} ${BOOTLOADER_PATH} cat "${BOOTLOADER_PATH}" >> "${HEX_FILE_WITH_BOOTLOADER_PATH}" ln -sf -- "${HEX_FILE_WITH_BOOTLOADER_PATH}" "${OUTPUT_PATH}/${SKETCH}-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 $@ } compile () { build_version build_filenames install -d "${OUTPUT_PATH}" echo "Building ${OUTPUT_DIR}/${SKETCH} (${LIB_VERSION}) ..." ${compile_HOOKS} if [ -d "${ARDUINO_LOCAL_LIB_PATH}/libraries" ]; then local_LIBS="-libraries \"${ARDUINO_LOCAL_LIB_PATH}/libraries\"" fi ARDUINO_PACKAGES="" if [ -d ${ARDUINO_PACKAGE_PATH} ]; then ARDUINO_PACKAGES="-hardware \"${ARDUINO_PACKAGE_PATH}\"" fi ${ARDUINO_BUILDER} \ -compile \ ${ARDUINO_PACKAGES} \ -hardware "${ARDUINO_PATH}/hardware" \ -hardware "${BOARD_HARDWARE_PATH}" \ ${ARDUINO_TOOLS_PARAM} \ -tools "${ARDUINO_PATH}/tools-builder" \ -fqbn "${FQBN}" \ -libraries "." \ -libraries "${ROOT}" \ -libraries "${BOARD_HARDWARE_PATH}/.." \ ${local_LIBS} \ ${EXTRA_BUILDER_ARGS} \ -build-path "${BUILD_PATH}" \ -ide-version "${ARDUINO_IDE_VERSION}" \ -prefs "compiler.cpp.extra_flags=-std=c++11 -Woverloaded-virtual -Wno-unused-parameter -Wno-unused-variable -Wno-ignored-qualifiers ${ARDUINO_CFLAGS} ${LOCAL_CFLAGS}" \ -warnings all \ ${ARDUINO_VERBOSE} \ ${ARDUINO_AVR_GCC_PREFIX_PARAM} \ "$(find_sketch)/${SKETCH}.ino" cp "${BUILD_PATH}/${SKETCH}.ino.hex" "${HEX_FILE_PATH}" cp "${BUILD_PATH}/${SKETCH}.ino.elf" "${ELF_FILE_PATH}" ln -sf "${OUTPUT_FILE_PREFIX}.hex" "${OUTPUT_PATH}/${SKETCH}-latest.hex" ln -sf "${OUTPUT_FILE_PREFIX}.elf" "${OUTPUT_PATH}/${SKETCH}-latest.elf" if [ "${ARDUINO_VERBOSE}" = "-verbose" ]; then echo "Build artifacts can be found in ${BUILD_PATH}"; else rm -rf "${BUILD_PATH}" fi } _find_all () { for plugin in ./*.ino \ examples/* \ src/*.ino; do if [ -d "$(dirname ${plugin})" ] || [ -f "${plugin}" ]; then p="$(basename "${plugin}" .ino)" if [ "${p}" != '*' ]; then echo "${p}" fi fi done | sort } build_all () { plugins="$(_find_all)" for plugin in ${plugins}; do export SKETCH="${plugin}" export LIBRARY="${plugin}" $0 ${plugin} build done } size () { if [ ! -e "${HEX_FILE_PATH}" ]; then compile fi echo "- Size: firmware/${LIBRARY}/${OUTPUT_FILE_PREFIX}.elf" firmware_size "${AVR_SIZE}" -C --mcu="${MCU}" "${ELF_FILE_PATH}" echo } size_map () { if [ ! -e "${HEX_FILE_PATH}" ]; then compile fi "${AVR_NM}" --size-sort -C -r -l -t decimal "${ELF_FILE_PATH}" } disassemble () { if [ ! -e "${HEX_FILE_PATH}" ]; then compile fi "${AVR_OBJDUMP}" -C -d "${ELF_FILE_PATH}" } decompile () { disassemble } clean () { rm -rf -- "${OUTPUT_PATH}" } reset_device() { find_device_port check_device_port reset_device_cmd } check_device_port () { if [ -z "$DEVICE_PORT" ]; then cat <&2 Couldn't autodetect the keyboard's serial port. If you see this message and your keyboard is connected to your computer, it probably means that our serial port detection logic is buggy or incomplete. 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 $DEVICE_PORT is not writable: `ls -l $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 his 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. build-all Build all Sketches we can find. EOF } help () { usage } if [ $# -lt 1 ]; then usage exit 1 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 ROOT="$(cd $(dirname $0)/..; pwd)" export ROOT export SOURCEDIR="$(pwd)" if [ -e "${HOME}/.kaleidoscope-builder.conf" ]; then . "${HOME}/.kaleidoscope-builder.conf" fi if [ -e "${SOURCEDIR}/.kaleidoscope-builder.conf" ]; then . "${SOURCEDIR}/.kaleidoscope-builder.conf" fi if [ -e "${SOURCEDIR}/kaleidoscope-builder.conf" ]; then . "${SOURCEDIR}/kaleidoscope-builder.conf" fi . ${ROOT}/etc/kaleidoscope-builder.conf if [ ! -z "${VERBOSE}" ] && [ "${VERBOSE}" -gt 0 ]; then ARDUINO_VERBOSE="-verbose" else ARDUINO_VERBOSE="-quiet" fi cmds="" ## Export vars for i in $(seq 1 $#); do v="$1" shift case "${v}" in *=*) export ${v} ;; *) cmds="${cmds} ${v}" ;; esac done set -- ${cmds} if [ $# -eq 1 ]; then cmd="$(echo $1 | tr '-' '_')" ${cmd} exit $? fi SKETCH="$1" shift if [ "${SKETCH}" = "default" ]; then SKETCH="${DEFAULT_SKETCH}" fi cmds="" for i in $(seq 1 $#); do cmds="${cmds} $(echo $1 | tr '-' '_')" shift done LIBRARY="${SKETCH}" export SKETCH export LIBRARY for cmd in ${cmds}; do ${cmd} done