use Action CI to build all examples (#403)
This commit is contained in:
parent
43fa67c387
commit
1924bc8fcf
4 changed files with 227 additions and 42 deletions
56
.github/workflows/githubci.yml
vendored
Normal file
56
.github/workflows/githubci.yml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
name: Build
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arduino-platform: ['feather52832', 'feather52840', 'cplaynrf52840', 'itsybitsy52840', 'cluenrf52840' ]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||
git submodule sync --recursive
|
||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive
|
||||
|
||||
- name: Install Arduino CLI and Tools
|
||||
run: |
|
||||
pip3 install adafruit-nrfutil
|
||||
# make all our directories we need for files and libraries
|
||||
mkdir $HOME/.arduino15
|
||||
mkdir $HOME/.arduino15/packages
|
||||
mkdir $HOME/Arduino
|
||||
mkdir $HOME/Arduino/libraries
|
||||
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
|
||||
echo "::add-path::$GITHUB_WORKSPACE/bin"
|
||||
|
||||
- name: Install BSP and Libraries
|
||||
env:
|
||||
BSP_URL: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
|
||||
run: |
|
||||
arduino-cli config init
|
||||
arduino-cli core update-index
|
||||
arduino-cli core update-index --additional-urls $BSP_URL
|
||||
arduino-cli core install adafruit:nrf52 --additional-urls $BSP_URL
|
||||
BSP_PATH=$HOME/.arduino15/packages/adafruit/hardware/nrf52
|
||||
BSP_VERSION=`eval ls $BSP_PATH`
|
||||
rm -r $BSP_PATH/*
|
||||
ln -s $GITHUB_WORKSPACE $BSP_PATH/$BSP_VERSION
|
||||
arduino-cli lib install "Adafruit NeoPixel" "Adafruit NeoMatrix" "Adafruit GFX Library" "Adafruit SSD1306" "MIDI Library" "Adafruit ILI9341" "Adafruit HX8357 Library" "Adafruit Circuit Playground" "Firmata"
|
||||
|
||||
- name: Build examples
|
||||
run: python3 tools/build_all.py ${{ matrix.arduino-platform }}
|
||||
|
|
@ -65,4 +65,4 @@ install:
|
|||
before_script:
|
||||
|
||||
script:
|
||||
- python3 tools/build_all.py
|
||||
- python3 tools/build_all_travis.py
|
||||
|
|
|
|||
|
|
@ -23,29 +23,33 @@ build_format = '| {:20} | {:30} | {:9} '
|
|||
build_separator = '-' * 78
|
||||
|
||||
variants_dict = {
|
||||
'feather52832': 'Feather nRF52832',
|
||||
'feather52840': 'Feather nRF52840 Express',
|
||||
'cplaynrf52840': 'Circuit Playground Bluefruit Express',
|
||||
'itsybitsy52840': 'ItsyBitsy nRF52840 Express',
|
||||
'cluenrf52840': 'Clue nRF52840',
|
||||
'feather52832': 'Feather nRF52832'
|
||||
'cluenrf52840': 'CLUE nRF52840'
|
||||
}
|
||||
|
||||
# STDERR receives output that starts with the following text, none of which should be considered a warning or error...
|
||||
output_to_ignore = (
|
||||
'Picked up JAVA_TOOL_OPTIONS:',
|
||||
'Loading configuration...',
|
||||
'Initializing packages...',
|
||||
'Preparing boards...',
|
||||
'Verifying...',
|
||||
)
|
||||
all_variants = []
|
||||
|
||||
# build all variants if input not existed
|
||||
if len(sys.argv) > 1:
|
||||
if (sys.argv[1] in variants_dict):
|
||||
all_variants.append(sys.argv[1])
|
||||
else:
|
||||
print('\033[31INTERNAL ERR\033[0m - invalid variant name "{}"'.format(sys.argv[1]))
|
||||
sys.exit(-1)
|
||||
else:
|
||||
all_variants = list(variants_dict.keys())
|
||||
|
||||
print(all_variants)
|
||||
exit
|
||||
|
||||
def errorOutputFilter(line):
|
||||
if len(line) == 0:
|
||||
return False
|
||||
if line.isspace(): # Note: empty string does not match here!
|
||||
return False
|
||||
if line.startswith(output_to_ignore): # alternatively, can trim() each line, but that would create lots of short-lived strings...
|
||||
return False
|
||||
# TODO: additional items to remove?
|
||||
return True
|
||||
|
||||
|
|
@ -59,12 +63,8 @@ def build_examples(variant):
|
|||
print(build_separator)
|
||||
print((build_format + '| {:6} |').format('Library', 'Example', 'Result', 'Time'))
|
||||
print(build_separator)
|
||||
subprocess.run("arduino --board adafruit:nrf52:{}:softdevice={},debug=l0 --save-prefs".format(variant, 's140v6' if variant != 'feather52832' else 's132v6'), shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if all_warnings:
|
||||
subprocess.run("arduino --pref 'compiler.warning_level=all' --save-prefs", shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
fqbn = "adafruit:nrf52:{}:softdevice={},debug=l0".format(variant, 's140v6' if variant != 'feather52832' else 's132v6')
|
||||
|
||||
for sketch in glob.iglob('libraries/**/*.ino', recursive=True):
|
||||
start_time = time.monotonic()
|
||||
|
|
@ -80,9 +80,9 @@ def build_examples(variant):
|
|||
# preferably, would use Python logging handler to get both distinct outputs and one merged output
|
||||
# for now, split STDERR when building with all warnings enabled, so can detect warning/error output.
|
||||
if all_warnings:
|
||||
build_result = subprocess.run("arduino --verify {}".format(sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
build_result = subprocess.run("arduino-cli compile --warnings all --fqbn {} {}".format(fqbn, sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
else:
|
||||
build_result = subprocess.run("arduino --verify {}".format(sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
build_result = subprocess.run("arduino-cli compile --warnings default --fqbn {} {}".format(fqbn, sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# get stderr into a form where len(warningLines) indicates a true warning was output to stderr
|
||||
warningLines = [];
|
||||
|
|
@ -104,9 +104,6 @@ def build_examples(variant):
|
|||
|
||||
build_duration = time.monotonic() - start_time
|
||||
|
||||
if travis:
|
||||
print('travis_fold:start:build-{}\\r'.format(sketch))
|
||||
|
||||
print((build_format + '| {:5.2f}s |').format(sketch.split(os.path.sep)[1], os.path.basename(sketch), success, build_duration))
|
||||
|
||||
if success != "skipped":
|
||||
|
|
@ -118,28 +115,11 @@ def build_examples(variant):
|
|||
for line in warningLines:
|
||||
print(line)
|
||||
|
||||
if travis:
|
||||
print('travis_fold:end:build-{}\\r'.format(sketch))
|
||||
|
||||
|
||||
build_time = time.monotonic()
|
||||
|
||||
|
||||
# build only one variant if the environment variable is specified
|
||||
if (ENV_VARIABLE_NAME in os.environ):
|
||||
variant = os.environ.get(ENV_VARIABLE_NAME)
|
||||
# only use the environment variable if the variant exists in the dictionary
|
||||
if (variant in variants_dict):
|
||||
build_examples(variant)
|
||||
else:
|
||||
print('\033[31INTERNAL ERR\033[0m - invalid variant name "{}"'.format(variant))
|
||||
fail_count += 1
|
||||
exit_status = -1
|
||||
|
||||
else: # no environment variable specified, so build all variants
|
||||
for var in variants_dict:
|
||||
build_examples(var)
|
||||
|
||||
for var in all_variants:
|
||||
build_examples(var)
|
||||
|
||||
print(build_separator)
|
||||
build_time = time.monotonic() - build_time
|
||||
|
|
|
|||
149
tools/build_all_travis.py
Normal file
149
tools/build_all_travis.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import os
|
||||
import glob
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
travis = False
|
||||
if "TRAVIS" in os.environ and os.environ["TRAVIS"] == "true":
|
||||
travis = True
|
||||
|
||||
all_warnings = False
|
||||
if "ALL_WARNINGS" in os.environ and os.environ["ALL_WARNINGS"] == "true":
|
||||
all_warnings = True
|
||||
|
||||
ENV_VARIABLE_NAME = 'VARIANT'
|
||||
|
||||
|
||||
exit_status = 0
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
|
||||
build_format = '| {:20} | {:30} | {:9} '
|
||||
build_separator = '-' * 78
|
||||
|
||||
variants_dict = {
|
||||
'feather52840': 'Feather nRF52840 Express',
|
||||
'cplaynrf52840': 'Circuit Playground Bluefruit Express',
|
||||
'itsybitsy52840': 'ItsyBitsy nRF52840 Express',
|
||||
'cluenrf52840': 'Clue nRF52840',
|
||||
'feather52832': 'Feather nRF52832'
|
||||
}
|
||||
|
||||
# STDERR receives output that starts with the following text, none of which should be considered a warning or error...
|
||||
output_to_ignore = (
|
||||
'Picked up JAVA_TOOL_OPTIONS:',
|
||||
'Loading configuration...',
|
||||
'Initializing packages...',
|
||||
'Preparing boards...',
|
||||
'Verifying...',
|
||||
)
|
||||
|
||||
def errorOutputFilter(line):
|
||||
if len(line) == 0:
|
||||
return False
|
||||
if line.isspace(): # Note: empty string does not match here!
|
||||
return False
|
||||
if line.startswith(output_to_ignore): # alternatively, can trim() each line, but that would create lots of short-lived strings...
|
||||
return False
|
||||
# TODO: additional items to remove?
|
||||
return True
|
||||
|
||||
|
||||
def build_examples(variant):
|
||||
global exit_status, success_count, fail_count, build_format, build_separator
|
||||
|
||||
print('\n')
|
||||
print(build_separator)
|
||||
print('| {:^74} |'.format(variants_dict[variant]))
|
||||
print(build_separator)
|
||||
print((build_format + '| {:6} |').format('Library', 'Example', 'Result', 'Time'))
|
||||
print(build_separator)
|
||||
subprocess.run("arduino --board adafruit:nrf52:{}:softdevice={},debug=l0 --save-prefs".format(variant, 's140v6' if variant != 'feather52832' else 's132v6'), shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if all_warnings:
|
||||
subprocess.run("arduino --pref 'compiler.warning_level=all' --save-prefs", shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
for sketch in glob.iglob('libraries/**/*.ino', recursive=True):
|
||||
start_time = time.monotonic()
|
||||
|
||||
# skip if example contains: ".skip" or ".skip.variant"
|
||||
# however ".build.variant" file can overwrite ".skip", used to build a specific variant only
|
||||
sketchdir = os.path.dirname(sketch)
|
||||
if ( (os.path.exists(sketchdir + '/.skip') or os.path.exists(sketchdir + '/.skip.' + variant)) and
|
||||
not os.path.exists(sketchdir + '/.build.' + variant)):
|
||||
success = "skipped"
|
||||
else:
|
||||
# TODO - preferably, would have STDERR show up in **both** STDOUT and STDERR.
|
||||
# preferably, would use Python logging handler to get both distinct outputs and one merged output
|
||||
# for now, split STDERR when building with all warnings enabled, so can detect warning/error output.
|
||||
if all_warnings:
|
||||
build_result = subprocess.run("arduino --verify {}".format(sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
else:
|
||||
build_result = subprocess.run("arduino --verify {}".format(sketch), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# get stderr into a form where len(warningLines) indicates a true warning was output to stderr
|
||||
warningLines = [];
|
||||
if all_warnings and build_result.stderr:
|
||||
tmpWarningLines = build_result.stderr.decode("utf-8").splitlines()
|
||||
warningLines = list(filter(errorOutputFilter, (tmpWarningLines)))
|
||||
|
||||
if build_result.returncode != 0:
|
||||
exit_status = build_result.returncode
|
||||
success = "\033[31mfailed\033[0m "
|
||||
fail_count += 1
|
||||
elif len(warningLines) != 0:
|
||||
exit_status = -1
|
||||
success = "\033[31mwarnings\033[0m "
|
||||
fail_count += 1
|
||||
else:
|
||||
success = "\033[32msucceeded\033[0m"
|
||||
success_count += 1
|
||||
|
||||
build_duration = time.monotonic() - start_time
|
||||
|
||||
if travis:
|
||||
print('travis_fold:start:build-{}\\r'.format(sketch))
|
||||
|
||||
print((build_format + '| {:5.2f}s |').format(sketch.split(os.path.sep)[1], os.path.basename(sketch), success, build_duration))
|
||||
|
||||
if success != "skipped":
|
||||
if build_result.returncode != 0:
|
||||
print(build_result.stdout.decode("utf-8"))
|
||||
if (build_result.stderr):
|
||||
print(build_result.stderr.decode("utf-8"))
|
||||
if len(warningLines) != 0:
|
||||
for line in warningLines:
|
||||
print(line)
|
||||
|
||||
if travis:
|
||||
print('travis_fold:end:build-{}\\r'.format(sketch))
|
||||
|
||||
|
||||
build_time = time.monotonic()
|
||||
|
||||
|
||||
# build only one variant if the environment variable is specified
|
||||
if (ENV_VARIABLE_NAME in os.environ):
|
||||
variant = os.environ.get(ENV_VARIABLE_NAME)
|
||||
# only use the environment variable if the variant exists in the dictionary
|
||||
if (variant in variants_dict):
|
||||
build_examples(variant)
|
||||
else:
|
||||
print('\033[31INTERNAL ERR\033[0m - invalid variant name "{}"'.format(variant))
|
||||
fail_count += 1
|
||||
exit_status = -1
|
||||
|
||||
else: # no environment variable specified, so build all variants
|
||||
for var in variants_dict:
|
||||
build_examples(var)
|
||||
|
||||
|
||||
print(build_separator)
|
||||
build_time = time.monotonic() - build_time
|
||||
print("Build Summary: {} \033[32msucceeded\033[0m, {} \033[31mfailed\033[0m and took {:.2f}s".format(success_count, fail_count, build_time))
|
||||
print(build_separator)
|
||||
|
||||
sys.exit(exit_status)
|
||||
Loading…
Reference in a new issue