-
Notifications
You must be signed in to change notification settings - Fork 7
/
arduino-ci-script.sh
2500 lines (2167 loc) · 144 KB
/
arduino-ci-script.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# This script is used to automate continuous integration tasks for Arduino projects
# https://github.com/per1234/arduino-ci-script
# Based on https://github.com/adafruit/travis-ci-arduino/blob/eeaeaf8fa253465d18785c2bb589e14ea9893f9f/install.sh#L11
# It seems that arrays can't been seen in other functions. So instead I'm setting $IDE_VERSIONS to a string that is the command to create the array
readonly ARDUINO_CI_SCRIPT_IDE_VERSION_LIST_ARRAY_DECLARATION="declare -a -r IDEversionListArray="
readonly ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER="${HOME}/temporary/arduino-ci-script"
readonly ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER="arduino"
readonly ARDUINO_CI_SCRIPT_VERIFICATION_OUTPUT_FILENAME="${ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER}/verification_output.txt"
readonly ARDUINO_CI_SCRIPT_REPORT_FILENAME="travis_ci_job_report_$(printf '%05d\n' "${TRAVIS_BUILD_NUMBER}").$(printf '%03d\n' "$(echo "$TRAVIS_JOB_NUMBER" | cut -d'.' -f 2)").tsv"
readonly ARDUINO_CI_SCRIPT_REPORT_FOLDER="${HOME}/arduino-ci-script_report"
readonly ARDUINO_CI_SCRIPT_REPORT_FILE_PATH="${ARDUINO_CI_SCRIPT_REPORT_FOLDER}/${ARDUINO_CI_SCRIPT_REPORT_FILENAME}"
# The arduino manpage(https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc#exit-status) documents a range of exit statuses. These exit statuses indicate success, invalid arduino command, or compilation failed due to legitimate code errors. arduino sometimes returns other exit statuses that may indicate problems that may go away after a retry.
readonly ARDUINO_CI_SCRIPT_HIGHEST_ACCEPTABLE_ARDUINO_EXIT_STATUS=4
readonly ARDUINO_CI_SCRIPT_SKETCH_VERIFY_RETRIES=3
readonly ARDUINO_CI_SCRIPT_REPORT_PUSH_RETRIES=10
readonly ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS=0
readonly ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS=1
# Arduino IDE 1.8.2 and newer generates a ton of garbage output (appears to be something related to jmdns) that must be filtered for the log to be readable and to avoid exceeding the maximum log length
readonly ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX='(^\[SocketListener\(travis-job-*|^ *[0-9][0-9]*: [0-9a-g][0-9a-g]*|^dns\[query,[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*:[0-9][0-9]*, length=[0-9][0-9]*, id=|^dns\[response,[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*:[0-9][0-9]*, length=[0-9][0-9]*, id=|^questions:$|\[DNSQuestion@|type: TYPE_IGNORE|^\.\]$|^\.\]\]$|^.\.\]$|^.\.\]\]$)'
# Default value
ARDUINO_CI_SCRIPT_TOTAL_SKETCH_BUILD_FAILURE_COUNT=0
# Set the arduino command name according to OS (on Windows arduino_debug should be used)
if [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]]; then
ARDUINO_CI_SCRIPT_ARDUINO_COMMAND="arduino_debug"
else
ARDUINO_CI_SCRIPT_ARDUINO_COMMAND="arduino"
fi
# Create the folder if it doesn't exist
function create_folder() {
local -r folderName="$1"
if ! [[ -d "$folderName" ]]; then
# shellcheck disable=SC2086
mkdir --parents $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "$folderName"
fi
}
function set_script_verbosity() {
ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL="$1"
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" == "true" ]]; then
ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL=1
fi
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -eq 1 ]]; then
ARDUINO_CI_SCRIPT_VERBOSITY_OPTION="--verbose"
ARDUINO_CI_SCRIPT_QUIET_OPTION=""
# Show stderr only
ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT="1>/dev/null"
elif [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -eq 2 ]]; then
ARDUINO_CI_SCRIPT_VERBOSITY_OPTION="--verbose"
ARDUINO_CI_SCRIPT_QUIET_OPTION=""
# Show stdout and stderr
ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT=""
else
ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL=0
ARDUINO_CI_SCRIPT_VERBOSITY_OPTION=""
# cabextract only takes the short option name so this is more universally useful than --quiet
ARDUINO_CI_SCRIPT_QUIET_OPTION="-q"
# Don't show stderr or stdout
ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT="&>/dev/null"
fi
}
# Deprecated, use set_script_verbosity
function set_verbose_script_output() {
set_script_verbosity 1
}
# Deprecated, use set_script_verbosity
function set_more_verbose_script_output() {
set_script_verbosity 2
}
# Turn on verbosity based on the preferences set by set_script_verbosity
function enable_verbosity() {
# Store previous verbosity settings so they can be set back to their original values at the end of the function
shopt -q -o verbose
ARDUINO_CI_SCRIPT_PREVIOUS_VERBOSE_SETTING="$?"
shopt -q -o xtrace
ARDUINO_CI_SCRIPT_PREVIOUS_XTRACE_SETTING="$?"
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -gt 0 ]]; then
# "Print shell input lines as they are read."
# https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
set -o verbose
fi
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -gt 1 ]]; then
# "Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. The value of the PS4 variable is expanded and the resultant value is printed before the command and its expanded arguments."
# https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
set -o xtrace
fi
}
# Return verbosity settings to their previous values
function disable_verbosity() {
if [[ "$ARDUINO_CI_SCRIPT_PREVIOUS_VERBOSE_SETTING" == "0" ]]; then
set -o verbose
else
set +o verbose
fi
if [[ "$ARDUINO_CI_SCRIPT_PREVIOUS_XTRACE_SETTING" == "0" ]]; then
set -o xtrace
else
set +o xtrace
fi
}
# Verbosity and, in some cases, errexit must be disabled before an early return from a public function, this allows it to be done in a single line instead of two
function return_handler() {
local -r exitStatus="$1"
# If exit status is success and errexit is enabled then it must be disabled before exiting the script because errexit must be disabled by default and only enabled in the functions that specifically require it.
# If exit status is not success then errexit should not be disabled, otherwise Travis CI won't fail the build even though the exit status was failure.
if [[ "$exitStatus" == "$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS" ]] && shopt -q -o errexit; then
set +o errexit
fi
disable_verbosity
return "$exitStatus"
}
function set_application_folder() {
enable_verbosity
ARDUINO_CI_SCRIPT_APPLICATION_FOLDER="$1"
disable_verbosity
}
function set_sketchbook_folder() {
enable_verbosity
ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER="$1"
# Create the sketchbook folder if it doesn't already exist
create_folder "$ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER"
# Set sketchbook location preference if the IDE is already installed
if [[ "$INSTALLED_IDE_VERSION_LIST_ARRAY" != "" ]]; then
set_ide_preference "sketchbook.path=$ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER"
fi
disable_verbosity
}
# Deprecated
function set_parameters() {
set_application_folder "$1"
set_sketchbook_folder "$2"
}
# Check for errors with the board definition that don't affect sketch verification
function set_board_testing() {
enable_verbosity
ARDUINO_CI_SCRIPT_TEST_BOARD="$1"
disable_verbosity
}
# Check for errors with libraries that don't affect sketch verification
function set_library_testing() {
enable_verbosity
ARDUINO_CI_SCRIPT_TEST_LIBRARY="$1"
disable_verbosity
}
# Install all specified versions of the Arduino IDE
function install_ide() {
enable_verbosity
local -r startIDEversion="$1"
local -r endIDEversion="$2"
# https://docs.travis-ci.com/user/customizing-the-build/#Implementing-Complex-Build-Steps
# set -o errexit will cause the script to exit as soon as any command returns a non-zero exit status. Without this the success of the function call is determined by the exit status of the last command in the function
set -o errexit
if [[ "$ARDUINO_CI_SCRIPT_APPLICATION_FOLDER" == "" ]]; then
echo "ERROR: Application folder was not set. Please use the set_application_folder function to define the location of the application folder."
return_handler "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
# Generate an array declaration string containing a list all available Arduino IDE versions which support CLI
# Save the current folder
local -r previousFolder="$PWD"
cd "$ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER"
# Create empty local repo for the purpose of getting a list of tags in the arduino/Arduino repository
git init --quiet Arduino
cd Arduino
git remote add origin https://github.com/arduino/Arduino.git
if [[ "$startIDEversion" != "1.6.2" ]] && [[ "$startIDEversion" != "1.6.2" ]]; then
# See "Arduino IDE version blocklist" documentation below
local -r IDEversion162regex=--regex='refs/tags/1\.6\.2'
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -gt 0 ]]; then
echo "NOTE: Due to not playing nicely with other versions, Arduino IDE 1.6.2 will not be installed unless explicitly specified in the version arguments."
fi
fi
# Arduino IDE tag blocklist:
# <1.5.2: no CLI (https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc#history)
# 1.5.4-r2: Not available for download
# 1.5.5-r2: Not available for download
# 1.5.7-macosx-java7: Not available for download
# 1.5.8-macosx-java7: Not available for download
# 1.6.2: has the nasty behavior of moving the included hardware cores to the .arduino15 folder, causing those versions to be used for all builds after Arduino IDE 1.6.2 is used. For that reason, 1.6.2 will only be installed if explicitly specified in the install_ide version arguments
# 1.6.5-r2: Not available for download
# 1.6.5-r3: Not available for download
# 1.6.5-r2: Not available for download
# 1.6.5-r3: Not available for download
# 1.8.11-ms-store-1: Not available for download
# 1.8.13-ms-store-1: Not available for download
local -r ARDUINO_CI_SCRIPT_FULL_IDE_VERSION_LIST_ARRAY="${ARDUINO_CI_SCRIPT_IDE_VERSION_LIST_ARRAY_DECLARATION}'(\"$(git ls-remote --quiet --tags --refs | grep --invert-match --regexp='refs/tags/1\.0' --regexp='refs/tags/1\.5$' --regexp='refs/tags/1\.5\.1$' --regexp='refs/tags/1\.5\.4-r2$' --regexp='refs/tags/1\.5\.5-r2$' --regexp='refs/tags/1\.5\.7-macosx-java7$' --regexp='refs/tags/1\.5\.8-macosx-java7$' ${IDEversion162regex} --regexp='refs/tags/1\.6\.5-r2$' --regexp='refs/tags/1\.6\.5-r3$' --regexp='refs/tags/.*-ms-store.*$' | grep --regexp='refs/tags/[0-9]\+\.[0-9]\+\.[0-9]\+\(\(-.*$\)\|$\)' | cut --delimiter='/' --fields=3 | sort --version-sort | sed ':a;N;$!ba;s/\n/\" \"/g')\")'"
cd ..
# Remove the temporary repo
rm Arduino --recursive --force
# Go back to the previous folder location
cd "$previousFolder"
# Determine list of IDE versions to install
generate_ide_version_list_array "$ARDUINO_CI_SCRIPT_FULL_IDE_VERSION_LIST_ARRAY" "$startIDEversion" "$endIDEversion"
INSTALLED_IDE_VERSION_LIST_ARRAY="$ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY"
# Set "$NEWEST_INSTALLED_IDE_VERSION"
determine_ide_version_extremes "$INSTALLED_IDE_VERSION_LIST_ARRAY"
NEWEST_INSTALLED_IDE_VERSION="$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION"
create_folder "$ARDUINO_CI_SCRIPT_APPLICATION_FOLDER"
# This runs the command contained in the $INSTALLED_IDE_VERSION_LIST_ARRAY string, thus declaring the array locally as $IDEversionListArray. This must be done in any function that uses the array
# Dummy declaration to fix the "referenced but not assigned" warning.
local IDEversionListArray
eval "$INSTALLED_IDE_VERSION_LIST_ARRAY"
# Determine whether any of the IDE versions to be installed require the creation of a virtual framebuffer (https://github.com/arduino/Arduino/blob/54264124b72eec40aaa22e327c16760f5e806c2a/build/shared/manpage.adoc#bugs)
# This is necessary in Arduino IDE 1.6.13 and older (https://github.com/arduino/Arduino/pull/5578) when running on a headless system
if [ -e /usr/bin/Xvfb ]; then
local -r virtualFramebufferRequiredRegex='^1\.[56]\.'
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
if [[ "$IDEversion" =~ $virtualFramebufferRequiredRegex ]]; then
# based on https://learn.adafruit.com/continuous-integration-arduino-and-you/testing-your-project
/sbin/start-stop-daemon --start $ARDUINO_CI_SCRIPT_QUIET_OPTION $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
sleep 3
export DISPLAY=:1.0
break
fi
done
fi
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
local IDEinstallFolder="$ARDUINO_CI_SCRIPT_APPLICATION_FOLDER/arduino-${IDEversion}"
# Don't unnecessarily install the IDE
if ! [[ -d "$IDEinstallFolder" ]]; then
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -eq 0 ]]; then
# If the download/installation process is going slowly when installing a lot of IDE versions this function may cause the build to fail due to exceeding Travis CI's 10 minutes without log output timeout so it's necessary to periodically print something.
echo "Installing: $IDEversion"
fi
# Determine download file extension
local tgzExtensionVersionsRegex="^1\.5\.[0-9]$"
if [[ "$IDEversion" =~ $tgzExtensionVersionsRegex ]]; then
# The download file extension prior to 1.6.0 is .tgz
local downloadFileExtension="tgz"
else
local downloadFileExtension="tar.xz"
fi
if [[ "$IDEversion" == "hourly" ]]; then
# Deal with the inaccurate name given to the hourly build download
local downloadVersion="nightly"
else
local downloadVersion="$IDEversion"
fi
wget --no-verbose $ARDUINO_CI_SCRIPT_QUIET_OPTION --directory-prefix="${ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER}/" "http://downloads.arduino.cc/arduino-${downloadVersion}-linux64.${downloadFileExtension}"
tar --extract --directory="$ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER" --file="${ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER}/arduino-${downloadVersion}-linux64.${downloadFileExtension}"
rm $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER}/arduino-${downloadVersion}-linux64.${downloadFileExtension}"
mv $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER}/arduino-${downloadVersion}" "$IDEinstallFolder"
fi
done
set_ide_preference "compiler.warning_level=all"
# If a sketchbook location has been defined then set the location in the Arduino IDE preferences
if [[ -d "$ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER" ]]; then
set_ide_preference "sketchbook.path=$ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER"
fi
# Return errexit to the default state
set +o errexit
disable_verbosity
}
# Generate an array of Arduino IDE versions as a subset of the list provided in the base array defined by the start and end versions
# This function allows the same code to be shared by install_ide and build_sketch. The generated array is "returned" as a global named "$ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY"
function generate_ide_version_list_array() {
local -r baseIDEversionArray="$1"
local startIDEversion="$2"
local endIDEversion="$3"
# Convert "oldest" or "newest" to actual version numbers
determine_ide_version_extremes "$baseIDEversionArray"
if [[ "$startIDEversion" == "oldest" ]]; then
startIDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION"
elif [[ "$startIDEversion" == "newest" ]]; then
startIDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION"
fi
if [[ "$endIDEversion" == "oldest" ]]; then
endIDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION"
elif [[ "$endIDEversion" == "newest" ]]; then
endIDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION"
fi
if [[ "$startIDEversion" == "" || "$startIDEversion" == "all" ]]; then
# Use the full base array
ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY="$baseIDEversionArray"
else
local rawIDElist
local -r IDEversionListRegex='\('
if [[ "$startIDEversion" =~ $IDEversionListRegex ]]; then
# IDE versions list was supplied
# Convert it to a temporary array
local -r suppliedIDEversionListArray="${ARDUINO_CI_SCRIPT_IDE_VERSION_LIST_ARRAY_DECLARATION}${startIDEversion}"
eval "$suppliedIDEversionListArray"
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
# Convert any use of "oldest" or "newest" special version names to the actual version number
if [[ "$IDEversion" == "oldest" ]]; then
IDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION"
elif [[ "$IDEversion" == "newest" ]]; then
IDEversion="$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION"
fi
# Add the version to the array
rawIDElist="${rawIDElist} "'"'"$IDEversion"'"'
done
elif [[ "$endIDEversion" == "" ]]; then
# Only a single version was specified
rawIDElist="$rawIDElist"'"'"$startIDEversion"'"'
else
# A version range was specified
eval "$baseIDEversionArray"
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
if [[ "$IDEversion" == "$startIDEversion" ]]; then
# Start of the list reached, set a flag
local -r listIsStarted="true"
fi
if [[ "$listIsStarted" == "true" ]]; then
# Add the version to the list
rawIDElist="${rawIDElist} "'"'"$IDEversion"'"'
fi
if [[ "$IDEversion" == "$endIDEversion" ]]; then
# End of the list was reached, exit the loop
break
fi
done
fi
# Turn the raw IDE version list into an array
declare -a -r rawIDElistArray="(${rawIDElist})"
# Remove duplicates from list https://stackoverflow.com/a/13648438
# shellcheck disable=SC2207
readonly local uniqueIDElistArray=($(echo "${rawIDElistArray[@]}" | tr ' ' '\n' | sort --unique --version-sort | tr '\n' ' '))
# Generate ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY
ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY="$ARDUINO_CI_SCRIPT_IDE_VERSION_LIST_ARRAY_DECLARATION"'('
for uniqueIDElistArrayIndex in "${!uniqueIDElistArray[@]}"; do
ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY="${ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY} "'"'"${uniqueIDElistArray[$uniqueIDElistArrayIndex]}"'"'
done
ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY="$ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY"')'
fi
}
# Determine the oldest and newest (non-hourly unless hourly is the only version on the list) IDE version in the provided array
# The determined versions are "returned" by setting the global variables "$ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION" and "$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION"
function determine_ide_version_extremes() {
local -r baseIDEversionArray="$1"
# Reset the variables from any value they were assigned the last time the function was ran
ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION=""
ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION=""
# Determine the oldest and newest (non-hourly) IDE version in the base array
eval "$baseIDEversionArray"
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
if [[ "$ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION" == "" ]]; then
ARDUINO_CI_SCRIPT_DETERMINED_OLDEST_IDE_VERSION="$IDEversion"
fi
if [[ "$ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION" == "" || "$IDEversion" != "hourly" ]]; then
ARDUINO_CI_SCRIPT_DETERMINED_NEWEST_IDE_VERSION="$IDEversion"
fi
done
}
function set_ide_preference() {
local -r preferenceString="$1"
# --pref option is only supported by Arduino IDE 1.5.6 and newer
local -r unsupportedPrefOptionVersionsRegex="^1\.5\.[0-5]$"
if ! [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedPrefOptionVersionsRegex ]]; then
install_ide_version "$NEWEST_INSTALLED_IDE_VERSION"
# --save-prefs was added in Arduino IDE 1.5.8
local -r unsupportedSavePrefsOptionVersionsRegex="^1\.5\.[6-7]$"
if ! [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedSavePrefsOptionVersionsRegex ]]; then
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --pref "$preferenceString" --save-prefs "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
else
# Arduino IDE 1.5.6 - 1.5.7 load the GUI if you only set preferences without doing a verify. So I am doing an unnecessary verification just to set the preferences in those versions. Definitely a hack but I prefer to keep the preferences setting code all here instead of cluttering build_sketch and this will pretty much never be used.
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --pref "$preferenceString" --verify "${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/arduino/examples/01.Basics/BareMinimum/BareMinimum.ino" "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
fi
fi
}
function install_ide_version() {
local -r IDEversion="$1"
# Create a symbolic link so that the Arduino IDE can always be referenced by the user from the same path no matter which version is being used.
if [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "msys" ]]; then
# git-bash's ln just does a copy instead of making a symlink, which takes forever and fails when the target folder exists (despite --force), which takes forever.
# Therefore, use the native Windows command mklink to create a directory junction instead.
# Using a directory junction instead of symlink because supposedly a symlink requires admin privileges.
# Windows doesn't seem to provide any way to overwrite directory junctions
if [[ -d "${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}" ]]; then
rm --recursive --force "${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER:?}"
fi
# https://stackoverflow.com/a/25394801
cmd <<<"mklink /J \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}\\${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}\" \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER//\//\\}\\arduino-${IDEversion}\"" >/dev/null
else
ln --symbolic --force --no-dereference $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/arduino-${IDEversion}" "${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}"
fi
}
# Install hardware packages
function install_package() {
enable_verbosity
set -o errexit
local -r URLregex="://"
if [[ "$1" =~ $URLregex ]]; then
# First argument is a URL, do a manual hardware package installation
# Note: Assumes the package is in the root of the download and has the correct folder structure (e.g. architecture folder added in Arduino IDE 1.5+)
local -r packageURL="$1"
# Create the hardware folder if it doesn't exist
create_folder "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware"
if [[ "$packageURL" =~ \.git$ ]]; then
# Clone the repository
local -r branchName="$2"
local -r previousFolder="$PWD"
cd "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware"
if [[ "$branchName" == "" ]]; then
git clone --quiet "$packageURL"
else
git clone --quiet --branch "$branchName" "$packageURL"
fi
cd "$previousFolder"
else
local -r previousFolder="$PWD"
cd "$ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER"
# Delete everything from the temporary folder
find ./ -mindepth 1 -delete
# Download the package
wget --no-verbose $ARDUINO_CI_SCRIPT_QUIET_OPTION "$packageURL"
# Uncompress the package
extract ./*.*
# Delete all files from the temporary folder
find ./ -maxdepth 1 -type f -delete
# Install the package
mv $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION ./* "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware/"
cd "$previousFolder"
fi
elif [[ "$1" == "" ]]; then
# Install hardware package from this repository
# https://docs.travis-ci.com/user/environment-variables#Global-Variables
local packageName
packageName="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 2)"
mkdir --parents $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware/$packageName"
local -r previousFolder="$PWD"
cd "$TRAVIS_BUILD_DIR"
cp --recursive $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION ./* "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware/${packageName}"
# * doesn't copy .travis.yml but that file will be present in the user's installation so it should be there for the tests too
cp $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${TRAVIS_BUILD_DIR}/.travis.yml" "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/hardware/${packageName}"
cd "$previousFolder"
else
# Install package via Boards Manager
local -r packageID="$1"
local -r packageURL="$2"
# Check if Arduino IDE is installed
if [[ "$INSTALLED_IDE_VERSION_LIST_ARRAY" == "" ]]; then
echo "ERROR: Installing a hardware package via Boards Manager requires the Arduino IDE to be installed. Please call install_ide before this command."
return_handler "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
# Check if the newest installed IDE version supports --install-boards
local -r unsupportedInstallBoardsOptionVersionsRange1regex="^1\.5\.[0-9]$"
local -r unsupportedInstallBoardsOptionVersionsRange2regex="^1\.6\.[0-3]$"
if [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedInstallBoardsOptionVersionsRange1regex ]] || [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedInstallBoardsOptionVersionsRange2regex ]]; then
echo "ERROR: --install-boards option is not supported by the newest version of the Arduino IDE you have installed. You must have Arduino IDE 1.6.4 or newer installed to use this function."
return_handler "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
else
# Temporarily install the latest IDE version to use for the package installation
install_ide_version "$NEWEST_INSTALLED_IDE_VERSION"
# If defined add the boards manager URL to preferences
if [[ "$packageURL" != "" ]]; then
# Get the current Additional Boards Manager URLs preference value so it won't be overwritten when the new URL is added
local priorBoardsmanagerAdditionalURLs
local getPrefExitStatus
# arduino --get-pref returns 4 when the preference does not exist, which is an acceptable circumstance. So it's necessary to unset errexit
set +o errexit
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -eq 0 ]]; then
priorBoardsmanagerAdditionalURLs=$(
"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}" --get-pref boardsmanager.additional.urls 2>/dev/null | tail --lines=1
exit "${PIPESTATUS[0]}"
)
getPrefExitStatus="$?"
elif [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -eq 1 ]]; then
priorBoardsmanagerAdditionalURLs=$(
"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}" --get-pref boardsmanager.additional.urls | tail --lines=1
exit "${PIPESTATUS[0]}"
)
getPrefExitStatus="$?"
else
priorBoardsmanagerAdditionalURLs=$(
"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}" --get-pref boardsmanager.additional.urls | tee /dev/tty | tail --lines=1
exit "${PIPESTATUS[0]}"
)
getPrefExitStatus="$?"
fi
set -o errexit
if [[ "$getPrefExitStatus" == "4" ]]; then
# No boardsmanager.additional.urls preference was set. This causes priorBoardsmanagerAdditionalURLs to have a garbage value with Arduino IDE 1.8.10 and newer.
priorBoardsmanagerAdditionalURLs=""
fi
local -r blankregex="^[ ]*$"
if [[ "$priorBoardsmanagerAdditionalURLs" =~ $blankregex ]]; then
# There is no previous Additional Boards Manager URLs preference value
local boardsmanagerAdditionalURLs="$packageURL"
else
# There is a previous Additional Boards Manager URLs preference value so append the new one to the end of it
local boardsmanagerAdditionalURLs="${priorBoardsmanagerAdditionalURLs},${packageURL}"
fi
# grep returns 1 when a line matches the regular expression so it's necessary to unset errexit
set +o errexit
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --pref boardsmanager.additional.urls="$boardsmanagerAdditionalURLs" --save-prefs "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT" | tr --complement --delete '[:print:]\n\t' | tr --squeeze-repeats '\n' | grep --extended-regexp --invert-match "$ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX"
local -r arduinoPreferenceSettingExitStatus="${PIPESTATUS[0]}"
set -o errexit
# this is required because otherwise the exit status of arduino is ignored
if [[ "$arduinoPreferenceSettingExitStatus" != "$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS" ]]; then
return_handler "$arduinoPreferenceSettingExitStatus"
fi
fi
# Install the package
# grep returns 1 when a line matches the regular expression so it's necessary to unset errexit
set +o errexit
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --install-boards "$packageID" "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT" | tr --complement --delete '[:print:]\n\t' | tr --squeeze-repeats '\n' | grep --extended-regexp --invert-match "$ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX"
local -r arduinoInstallPackageExitStatus="${PIPESTATUS[0]}"
set -o errexit
# this is required because otherwise the exit status of arduino is ignored
if [[ "$arduinoInstallPackageExitStatus" != "$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS" ]]; then
return_handler "$arduinoPreferenceSettingExitStatus"
fi
fi
fi
set +o errexit
disable_verbosity
}
function install_library() {
enable_verbosity
set -o errexit
local -r libraryIdentifier="$1"
# Create the libraries folder if it doesn't already exist
create_folder "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries"
local -r URLregex="://"
if [[ "$libraryIdentifier" =~ $URLregex ]]; then
# The argument is a URL
# Note: this assumes the library is in the root of the file
if [[ "$libraryIdentifier" =~ \.git$ ]]; then
# Clone the repository
local -r branchName="$2"
local -r newFolderName="$3"
local -r previousFolder="$PWD"
cd "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries"
if [[ "$branchName" == "" && "$newFolderName" == "" ]]; then
git clone --quiet "$libraryIdentifier"
elif [[ "$branchName" == "" ]]; then
git clone --quiet "$libraryIdentifier" "$newFolderName"
elif [[ "$newFolderName" == "" ]]; then
git clone --quiet --branch "$branchName" "$libraryIdentifier"
else
git clone --quiet --branch "$branchName" "$libraryIdentifier" "$newFolderName"
fi
cd "$previousFolder"
else
# Assume it's a compressed file
local -r newFolderName="$2"
# Download the file to the temporary folder
local -r previousFolder="$PWD"
cd "$ARDUINO_CI_SCRIPT_TEMPORARY_FOLDER"
# Delete everything from the temporary folder
find ./ -mindepth 1 -delete
wget --no-verbose $ARDUINO_CI_SCRIPT_QUIET_OPTION "$libraryIdentifier"
extract ./*.*
# Delete all files from the temporary folder
find ./ -maxdepth 1 -type f -delete
# Install the library
mv $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION ./* "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries/${newFolderName}"
cd "$previousFolder"
fi
elif [[ "$libraryIdentifier" == "" ]]; then
# Install library from the repository
# https://docs.travis-ci.com/user/environment-variables#Global-Variables
local libraryName
libraryName="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 2)"
mkdir --parents $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries/$libraryName"
local -r previousFolder="$PWD"
cd "$TRAVIS_BUILD_DIR"
cp --recursive $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION ./* "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries/${libraryName}"
# * doesn't copy .travis.yml but that file will be present in the user's installation so it should be there for the tests too
cp $ARDUINO_CI_SCRIPT_VERBOSITY_OPTION "${TRAVIS_BUILD_DIR}/.travis.yml" "${ARDUINO_CI_SCRIPT_SKETCHBOOK_FOLDER}/libraries/${libraryName}"
cd "$previousFolder"
else
# Install a library that is part of the Library Manager index
# Check if Arduino IDE is installed
if [[ "$INSTALLED_IDE_VERSION_LIST_ARRAY" == "" ]]; then
echo "ERROR: Installing a library via Library Manager requires the Arduino IDE to be installed. Please call install_ide before this command."
return_handler "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
# Check if the newest installed IDE version supports --install-library
local -r unsupportedInstallLibraryOptionVersionsRange1regex="^1\.5\.[0-9]$"
local -r unsupportedInstallLibraryOptionVersionsRange2regex="^1\.6\.[0-3]$"
if [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedInstallLibraryOptionVersionsRange1regex ]] || [[ "$NEWEST_INSTALLED_IDE_VERSION" =~ $unsupportedInstallLibraryOptionVersionsRange2regex ]]; then
echo "ERROR: --install-library option is not supported by the newest version of the Arduino IDE you have installed. You must have Arduino IDE 1.6.4 or newer installed to use this function."
return_handler "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
else
local -r libraryName="$1"
# Temporarily install the latest IDE version to use for the library installation
install_ide_version "$NEWEST_INSTALLED_IDE_VERSION"
# Install the library
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --install-library "$libraryName" "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
fi
fi
set +o errexit
disable_verbosity
}
# Extract common file formats
# https://github.com/xvoland/Extract
function extract() {
if [ -z "$1" ]; then
# display usage if no parameters given
echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
echo " extract <path/file_name_1.ext> [path/file_name_2.ext] [path/file_name_3.ext]"
return "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
else
local filename
for filename in "$@"; do
if [ -f "$filename" ]; then
case "${filename%,}" in
*.tar.bz2 | *.tar.gz | *.tar.xz | *.tbz2 | *.tgz | *.txz | *.tar)
tar --extract --file="$filename"
;;
*.lzma)
unlzma $ARDUINO_CI_SCRIPT_QUIET_OPTION ./"$filename"
;;
*.bz2)
bunzip2 $ARDUINO_CI_SCRIPT_QUIET_OPTION ./"$filename"
;;
*.rar)
eval unrar x -ad ./"$filename" "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
;;
*.gz)
gunzip ./"$filename"
;;
*.zip)
unzip -qq ./"$filename"
;;
*.z)
eval uncompress ./"$filename" "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
;;
*.7z | *.arj | *.cab | *.chm | *.deb | *.dmg | *.iso | *.lzh | *.msi | *.rpm | *.udf | *.wim | *.xar)
7z x ./"$filename"
;;
*.xz)
unxz $ARDUINO_CI_SCRIPT_QUIET_OPTION ./"$filename"
;;
*.exe)
cabextract $ARDUINO_CI_SCRIPT_QUIET_OPTION ./"$filename"
;;
*)
echo "extract: '$filename' - unknown archive method"
return "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
;;
esac
else
echo "extract: '$filename' - file does not exist"
return "$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
done
fi
}
function set_verbose_output_during_compilation() {
enable_verbosity
local -r verboseOutputDuringCompilation="$1"
if [[ "$verboseOutputDuringCompilation" == "true" ]]; then
ARDUINO_CI_SCRIPT_DETERMINED_VERBOSE_BUILD="--verbose"
else
ARDUINO_CI_SCRIPT_DETERMINED_VERBOSE_BUILD=""
fi
disable_verbosity
}
# Verify the sketch
function build_sketch() {
enable_verbosity
local -r sketchPath="$1"
local -r boardID="$2"
local -r allowFail="$3"
local -r startIDEversion="$4"
local -r endIDEversion="$5"
# Set default value for buildSketchExitStatus
local buildSketchExitStatus="$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS"
generate_ide_version_list_array "$INSTALLED_IDE_VERSION_LIST_ARRAY" "$startIDEversion" "$endIDEversion"
if [[ "$ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY" == "$ARDUINO_CI_SCRIPT_IDE_VERSION_LIST_ARRAY_DECLARATION"'()' ]]; then
echo "ERROR: The IDE version(s) specified are not installed"
buildSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
else
eval "$ARDUINO_CI_SCRIPT_GENERATED_IDE_VERSION_LIST_ARRAY"
local IDEversion
for IDEversion in "${IDEversionListArray[@]}"; do
# Install the IDE
# This must be done before searching for sketches in case the path specified is in the Arduino IDE installation folder
install_ide_version "$IDEversion"
# The package_index files installed by some versions of the IDE (1.6.5, 1.6.5) can cause compilation to fail for other versions (1.6.5-r4, 1.6.5-r5). Attempting to install a dummy package ensures that the correct version of those files will be installed before the sketch verification.
# Check if the newest installed IDE version supports --install-boards
local unsupportedInstallBoardsOptionVersionsRange1regex="^1\.5\.[0-9]$"
local unsupportedInstallBoardsOptionVersionsRange2regex="^1\.6\.[0-3]$"
if ! [[ "$IDEversion" =~ $unsupportedInstallBoardsOptionVersionsRange1regex ]] && ! [[ "$IDEversion" =~ $unsupportedInstallBoardsOptionVersionsRange2regex ]]; then
# shellcheck disable=SC2086
eval \"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}\" --install-boards arduino:dummy "$ARDUINO_CI_SCRIPT_VERBOSITY_REDIRECT"
if [[ "$ARDUINO_CI_SCRIPT_VERBOSITY_LEVEL" -gt 1 ]]; then
# The warning is printed to stdout
echo "NOTE: The warning above \"Selected board is not available\" is caused intentionally and does not indicate a problem."
fi
fi
if [[ "$sketchPath" =~ \.ino$ ]] || [[ "$sketchPath" =~ \.pde$ ]]; then
# A sketch was specified
if ! [[ -f "$sketchPath" ]]; then
echo "ERROR: Specified sketch: $sketchPath doesn't exist"
buildSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
elif ! build_this_sketch "$sketchPath" "$boardID" "$IDEversion" "$allowFail"; then
# build_this_sketch returned a non-zero exit status
buildSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
else
# Search for all sketches in the path and put them in an array
local sketchFound="false"
# https://github.com/koalaman/shellcheck/wiki/SC2207
declare -a sketches
mapfile -t sketches < <(find "$sketchPath" -name "*.pde" -o -name "*.ino")
local sketchName
for sketchName in "${sketches[@]}"; do
# Only verify the sketch that matches the name of the sketch folder, otherwise it will cause redundant verifications for sketches that have multiple .ino files
local sketchFolder
sketchFolder="$(echo "$sketchName" | rev | cut -d'/' -f 2 | rev)"
local sketchNameWithoutPathWithExtension
sketchNameWithoutPathWithExtension="$(echo "$sketchName" | rev | cut -d'/' -f 1 | rev)"
local sketchNameWithoutPathWithoutExtension
sketchNameWithoutPathWithoutExtension="${sketchNameWithoutPathWithExtension%.*}"
if [[ "$sketchFolder" == "$sketchNameWithoutPathWithoutExtension" ]]; then
sketchFound="true"
if ! build_this_sketch "$sketchName" "$boardID" "$IDEversion" "$allowFail"; then
# build_this_sketch returned a non-zero exit status
buildSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
fi
done
if [[ "$sketchFound" == "false" ]]; then
echo "ERROR: No valid sketches were found in the specified path"
buildSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
fi
fi
done
fi
disable_verbosity
return $buildSketchExitStatus
}
function build_this_sketch() {
# Fold this section of output in the Travis CI build log to make it easier to read
echo -e "travis_fold:start:build_sketch"
local -r sketchName="$1"
local -r boardID="$2"
local -r IDEversion="$3"
local -r allowFail="$4"
# Produce a useful label for the fold in the Travis log for this function call
echo "build_sketch $sketchName $boardID $allowFail $IDEversion"
# Arduino IDE 1.8.0 and 1.8.1 fail to verify a sketch if the absolute path to it is not specified
# http://stackoverflow.com/a/3915420/7059512
local absoluteSketchName
absoluteSketchName="$(
cd "$(dirname "$1")"
pwd
)/$(basename "$1")"
# Define a dummy value for arduinoExitStatus so that the while loop will run at least once
local arduinoExitStatus=255
# Retry the verification if arduino returns an exit status that indicates there may have been a temporary error not caused by a bug in the sketch or the arduino command
while [[ $arduinoExitStatus -gt $ARDUINO_CI_SCRIPT_HIGHEST_ACCEPTABLE_ARDUINO_EXIT_STATUS && $verifyCount -le $ARDUINO_CI_SCRIPT_SKETCH_VERIFY_RETRIES ]]; do
# Verify the sketch
# shellcheck disable=SC2086
"${ARDUINO_CI_SCRIPT_APPLICATION_FOLDER}/${ARDUINO_CI_SCRIPT_IDE_INSTALLATION_FOLDER}/${ARDUINO_CI_SCRIPT_ARDUINO_COMMAND}" $ARDUINO_CI_SCRIPT_DETERMINED_VERBOSE_BUILD --verify "$absoluteSketchName" --board "$boardID" 2>&1 | tr --complement --delete '[:print:]\n\t' | tr --squeeze-repeats '\n' | grep --extended-regexp --invert-match "$ARDUINO_CI_SCRIPT_ARDUINO_OUTPUT_FILTER_REGEX" | tee "$ARDUINO_CI_SCRIPT_VERIFICATION_OUTPUT_FILENAME"
local arduinoExitStatus="${PIPESTATUS[0]}"
local verifyCount=$((verifyCount + 1))
done
if [[ "$arduinoExitStatus" != "$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS" ]]; then
# Sketch verification failed
local buildThisSketchExitStatus="$ARDUINO_CI_SCRIPT_FAILURE_EXIT_STATUS"
else
# Sketch verification succeeded
local buildThisSketchExitStatus="$ARDUINO_CI_SCRIPT_SUCCESS_EXIT_STATUS"
# Parse through the output from the sketch verification to count warnings and determine the compile size
local warningCount=0
local boardIssueCount=0
local libraryIssueCount=0
while read -r outputFileLine; do
# Determine program storage memory usage
local programStorageRegex="Sketch uses ([0-9,]+) *"
if [[ "$outputFileLine" =~ $programStorageRegex ]] >/dev/null; then
local -r programStorageWithComma=${BASH_REMATCH[1]}
fi
# Determine dynamic memory usage
local dynamicMemoryRegex="Global variables use ([0-9,]+) *"
if [[ "$outputFileLine" =~ $dynamicMemoryRegex ]] >/dev/null; then
local -r dynamicMemoryWithComma=${BASH_REMATCH[1]}
fi
# Increment warning count
local warningRegex="warning: "
if [[ "$outputFileLine" =~ $warningRegex ]] >/dev/null; then
warningCount=$((warningCount + 1))
fi
# Check for board issues
local bootloaderMissingRegex="Bootloader file specified but missing: "
if [[ "$outputFileLine" =~ $bootloaderMissingRegex ]] >/dev/null; then
local boardIssue="missing bootloader"
boardIssueCount=$((boardIssueCount + 1))
fi
local boardsDotTxtMissingRegex="Could not find boards.txt"
if [[ "$outputFileLine" =~ $boardsDotTxtMissingRegex ]] >/dev/null; then
local boardIssue="Could not find boards.txt"
boardIssueCount=$((boardIssueCount + 1))
fi
local buildDotBoardNotDefinedRegex="doesn't define a 'build.board' preference"
if [[ "$outputFileLine" =~ $buildDotBoardNotDefinedRegex ]] >/dev/null; then
local boardIssue="doesn't define a 'build.board' preference"
boardIssueCount=$((boardIssueCount + 1))
fi
# Check for library issues
# This is the generic "invalid library" warning that doesn't specify the reason
local invalidLibrarRegex1="Invalid library found in"
local invalidLibrarRegex2="from library$"
if [[ "$outputFileLine" =~ $invalidLibrarRegex1 ]] && ! [[ "$outputFileLine" =~ $invalidLibrarRegex2 ]] >/dev/null; then
local libraryIssue="Invalid library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingNameRegex="Invalid library found in .* Missing 'name' from library"
if [[ "$outputFileLine" =~ $missingNameRegex ]] >/dev/null; then
local libraryIssue="Missing 'name' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingVersionRegex="Invalid library found in .* Missing 'version' from library"
if [[ "$outputFileLine" =~ $missingVersionRegex ]] >/dev/null; then
local libraryIssue="Missing 'version' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingAuthorRegex="Invalid library found in .* Missing 'author' from library"
if [[ "$outputFileLine" =~ $missingAuthorRegex ]] >/dev/null; then
local libraryIssue="Missing 'author' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingMaintainerRegex="Invalid library found in .* Missing 'maintainer' from library"
if [[ "$outputFileLine" =~ $missingMaintainerRegex ]] >/dev/null; then
local libraryIssue="Missing 'maintainer' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingSentenceRegex="Invalid library found in .* Missing 'sentence' from library"
if [[ "$outputFileLine" =~ $missingSentenceRegex ]] >/dev/null; then
local libraryIssue="Missing 'sentence' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingParagraphRegex="Invalid library found in .* Missing 'paragraph' from library"
if [[ "$outputFileLine" =~ $missingParagraphRegex ]] >/dev/null; then
local libraryIssue="Missing 'paragraph' from library"
libraryIssueCount=$((libraryIssueCount + 1))
fi
local missingURLregex="Invalid library found in .* Missing 'url' from library"