-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·512 lines (457 loc) · 16.3 KB
/
build.sh
File metadata and controls
executable file
·512 lines (457 loc) · 16.3 KB
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
#!/usr/bin/env bash
# an interface to running pio builds
# args can be combined, e.g. '-cbufm' and in any order.
# SEE build-sh.md FOR ADDITIONAL IMPORTANT INFORMATION ABOUT
# CONFIGURATION INI FILE USAGE
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
PIO_VENV_ROOT="${PLATFORMIO_CORE_DIR:-${HOME}/.platformio/penv}"
PC_VENV_ROOT="${SCRIPT_DIR}/build/.venv"
BUILD_ALL=0
RUN_BUILD=0
ENV_NAME=""
DO_CLEAN=0
SHOW_GRAPH=0
SHOW_MONITOR=0
SHOW_BOARDS=0
TARGET_NAME=""
PC_TARGET=""
DEBUG_PC_BUILD=0
UPLOAD_IMAGE=0
UPLOAD_FS=0
DEV_MODE=0
ZIP_MODE=0
AUTOCLEAN=1
SETUP_NEW_BOARD=""
ANSWER_YES=0
CMAKE_GENERATOR=""
INI_FILE="${SCRIPT_DIR}/platformio-generated.ini"
LOCAL_INI_VALUES_FILE="${SCRIPT_DIR}/platformio.local.ini"
# Function to check if the specified Python version is 3
check_python_version() {
local python_bin=$1
if ! command -v "${python_bin}" &> /dev/null; then
return 1
fi
# Extract the major version number
local major_version="$(${python_bin} --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1)"
# Verify if it's Python 3
if [ "${major_version}" -eq 3 ]; then
return 0
else
return 1
fi
}
function display_board_names {
while IFS= read -r piofile; do
BOARD_NAME=$(echo $(basename $piofile) | sed 's#^platformio-##;s#.ini$##')
echo "$BOARD_NAME"
done < <(find "$SCRIPT_DIR/build-platforms" -name 'platformio-*.ini' -print | sort)
}
function show_help {
echo "Usage: $(basename $0) [options] -- [additional args]"
echo ""
echo "fujinet-firmware (pio) options:"
echo " -c # run clean before build"
echo " -b # run build"
echo " -u # upload firmware"
echo " -f # upload filesystem (webUI etc)"
echo " -m # run monitor after build"
echo " -d # add dev flag to build"
echo " -e ENV # use specific environment"
echo " -t TGT # run target task (default of none means do build, but -b must be specified"
echo " -n # do not autoclean"
echo ""
echo "one-off firmware options"
echo " -a # build ALL target platforms to test changes work on all platforms"
echo " -z # build flashable zip"
echo ""
echo "fujinet-firmware board setup options:"
echo " -s NAME # Setup a new board from name, writes a new file 'platformio.local.ini'"
echo " -i FILE # use FILE as INI instead of platformio-generated.ini"
echo " -l FILE # use FILE to use instead of 'platform.local.ini'"
echo ""
echo "fujinet-pc (cmake) options:"
echo " -c # run clean before build"
echo " -p TGT # perform PC build instead of ESP, for given target (e.g. APPLE|ATARI)"
echo " -g # enable debug in generated fujinet-pc exe"
echo " -G GEN # Use GEN as the Generator for cmake (e.g. -G \"Unix Makefiles\" )"
echo ""
echo "other options:"
echo " -y # answers any questions with Y automatically, for unattended builds"
echo " -h # this help"
echo " -V # Override default Python virtual environment location (e.g. \"-V ~/.platformio/penv\")"
echo " # Alternatively, this can be set with the shell env var VENV_ROOT"
echo ""
echo "Additional Args can be accepted to pass values onto sub processes where supported."
echo " e.g. ./build.sh -p APPLE -- -DFOO=BAR"
echo ""
echo "Simple firmware builds:"
echo " ./build.sh -cb # for CLEAN + BUILD of current target in platformio-local.ini"
echo " ./build.sh -m # View FujiNet Monitor"
echo " ./build.sh -cbum # Clean/Build/Upload to FN/Monitor"
echo " ./build.sh -f # Upload filesystem"
echo ""
echo "Supported boards:"
echo ""
display_board_names
exit 1
}
if [ $# -eq 0 ] ; then
show_help
fi
while getopts "abcde:fgG:hi:l:mnp:s:St:uyzV:" flag
do
case "$flag" in
a) BUILD_ALL=1 ;;
b) RUN_BUILD=1 ;;
c) DO_CLEAN=1 ;;
d) DEV_MODE=1 ;;
e) ENV_NAME=${OPTARG} ;;
f) UPLOAD_FS=1 ;;
g) DEBUG_PC_BUILD=1 ;;
i) INI_FILE=${OPTARG} ;;
l) LOCAL_INI_VALUES_FILE=${OPTARG} ;;
m) SHOW_MONITOR=1 ;;
n) AUTOCLEAN=0 ;;
p) PC_TARGET=${OPTARG} ;;
t) TARGET_NAME=${OPTARG} ;;
s) SETUP_NEW_BOARD=${OPTARG} ;;
S) SHOW_BOARDS=1 ;;
u) UPLOAD_IMAGE=1 ;;
G) CMAKE_GENERATOR=${OPTARG} ;;
y) ANSWER_YES=1 ;;
z) ZIP_MODE=1 ;;
V) VENV_ROOT=${OPTARG} ;;
h) show_help ;;
*) show_help ;;
esac
done
shift $((OPTIND - 1))
# Requirements:
# - python3
# - python3 can create venv - PlatformIO also needs this to install penv
# - if doing ESP32 build:
# - PlatformIO
# - not ESP32 build:
# - cmake
# Make sure we have python3 and it has the ability to create venvs
PYTHON=python
if ! check_python_version "${PYTHON}" ; then
PYTHON=python3
if ! check_python_version "${PYTHON}" ; then
echo "Python 3 is not installed"
exit 1
fi
fi
if ! ${PYTHON} -c "import venv, ensurepip" 2>/dev/null ; then
echo "Error: Python venv module is not installed."
exit 1
fi
# if ! "$PYTHON" -m pip --version >/dev/null 2>&1; then
# echo Error: pip is not installed.
# exit 1
# fi
if [ -z "${VENV_ROOT}" ] ; then
if [ -n "${PC_TARGET}" ] ; then
VENV_ROOT="${PC_VENV_ROOT}"
else
VENV_ROOT="${PIO_VENV_ROOT}"
fi
fi
ACTIVATE="${VENV_ROOT}/bin/activate"
# For Windows/MSYS2
ALT_ACTIVATE="${VENV_ROOT}/Scripts/activate"
if [ -z "${PC_TARGET}" ] ; then
# Doing a PlatformIO build, locate PlatformIO. It may or may not
# already be in the users' path.
if [ -f "${ACTIVATE}" ] ; then
# Activate now in case pio isn't already in PATH
source "${ACTIVATE}"
fi
PIO=$(command -v pio)
if [ -z "${PIO}" ] ; then
echo Please install platformio
exit 1
fi
fi
# Let the user know about any required packages they need to install
MISSING=""
if [ -n "${PC_TARGET}" ] ; then
COMPILER=g++
if [ "$MSYSTEM" = "CLANG64" ]; then
COMPILER=clang++
fi
for REQUIRED in ${COMPILER} make cmake ; do
if ! command -v ${REQUIRED} > /dev/null ; then
MISSING="${REQUIRED} ${MISSING}"
fi
done
fi
if [ -n "${MISSING}" ] ; then
echo The following commands need to be installed: ${MISSING}
exit 1
fi
normalize_path() {
case "$OSTYPE" in
msys*|cygwin*)
cygpath --unix "$1"
;;
*)
# Already Unix-style, return as-is
echo "$1"
;;
esac
}
same_dir() {
[ -d "$1" ] && [ -d "$2" ] || return 1
stat1=$(stat -c "%d:%i" "$1")
stat2=$(stat -c "%d:%i" "$2")
[ "$stat1" = "$stat2" ]
}
if [[ "$VIRTUAL_ENV" != "$VENV_ROOT" ]] ; then
if [ ! -f "${ACTIVATE}" ] ; then
echo Creating venv at "${VENV_ROOT}"
mkdir -p $(dirname "${VENV_ROOT}")
${PYTHON} -m venv "${VENV_ROOT}" || exit 1
fi
if [ -f "${ACTIVATE}" ] ; then
source "${ACTIVATE}"
elif [ -f "${ALT_ACTIVATE}" ] ; then
source "${ALT_ACTIVATE}"
echo "-------------------"
cat "${ALT_ACTIVATE}"
echo "-------------------"
fi
VENV_ACTUAL="$(normalize_path "$VIRTUAL_ENV")"
if ! same_dir "${VENV_ACTUAL}" "${VENV_ROOT}" ; then
echo Unable to activate penv/venv
echo "ACTIVATE = ${ACTIVATE}"
echo "ALT_ACTIVATE = ${ALT_ACTIVATE}"
echo "VIRTAUL_ENV = ${VIRTUAL_ENV}"
echo "VENV_ACTUAL = ${VENV_ACTUAL}"
echo "VENV_ROOT = ${VENV_ROOT}"
ls -Fla "$(dirname ${ACTIVATE})" || true
ls -Fla "$(dirname ${ALT_ACTIVATE})" || true
exit 1
fi
fi
# If pio is the one installed by the system it runs the system
# python instead of the penv python, blocking pip from installing
# packages
if [ -z "${PC_BUILD}" ] ; then
PIO=$(command -v pio)
if [ "${PIO}" != "${VENV_ROOT}/bin/pio" ] ; then
pip install platformio || exit 1
fi
fi
echo Virtual env: "${VIRTUAL_ENV}"
echo venv root: "${VENV_ROOT}"
echo Activate: "${ACTIVATE}"
echo PATH: "${PATH}"
command -v pio
if [ $SHOW_BOARDS -eq 1 ] ; then
display_board_names
exit 1
fi
if [ $BUILD_ALL -eq 1 ] ; then
# BUILD ALL platforms and exit
chmod 755 $SCRIPT_DIR/build-platforms/build-all.sh
$SCRIPT_DIR/build-platforms/build-all.sh
exit $?
fi
##############################################################
# PC BUILD using cmake
if [ ! -z "$PC_TARGET" ] ; then
echo "PC Build Mode"
# lets build_webui.py know we are using the generated INI file, this variable name is the one PIO uses when it calls subprocesses, so we use same name.
export PROJECT_CONFIG=$INI_FILE
GEN_CMD=""
if [ -n "$CMAKE_GENERATOR" ] ; then
GEN_CMD="-G $CMAKE_GENERATOR"
fi
mkdir -p "$SCRIPT_DIR/build"
LAST_TARGET_FILE="$SCRIPT_DIR/build/last-target"
LAST_TARGET=""
if [ -f "${LAST_TARGET_FILE}" ]; then
LAST_TARGET=$(cat ${LAST_TARGET_FILE})
fi
if [[ (-n ${LAST_TARGET}) && ("${LAST_TARGET}" != "$PC_TARGET") ]] ; then
DO_CLEAN=1
fi
echo -n "$PC_TARGET" > ${LAST_TARGET_FILE}
if [ $DO_CLEAN -eq 1 ] ; then
echo "Removing old build artifacts"
rm -rf $SCRIPT_DIR/build/*
rm -f $SCRIPT_DIR/build/.ninja* 2>/dev/null
fi
cd $SCRIPT_DIR/build
# Write out the compile commands for clangd etc to use
if [ -z "$GEN_CMD" ]; then
cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DFUJINET_TARGET=$PC_TARGET "$@"
else
cmake "$GEN_CMD" .. -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DFUJINET_TARGET=$PC_TARGET "$@"
fi
if [ $? -ne 0 ]; then
echo "cmake failed writing compile commands. Exiting"
exit 1
fi
# Run the specific build
BUILD_TYPE="Release"
if [ $DEBUG_PC_BUILD -eq 1 ] ; then
BUILD_TYPE="Debug"
fi
echo "Building for $BUILD_TYPE"
if [ -z "$GEN_CMD" ]; then
cmake .. -DFUJINET_TARGET=$PC_TARGET -DCMAKE_BUILD_TYPE=$BUILD_TYPE "$@"
else
cmake "$GEN_CMD" .. -DFUJINET_TARGET=$PC_TARGET -DCMAKE_BUILD_TYPE=$BUILD_TYPE "$@"
fi
if [ $? -ne 0 ] ; then
echo "Error running initial cmake. Aborting"
exit 1
fi
# python_modules.txt contains pairs of module name and installable package names, separated by pipe symbol
MOD_LIST=$(sed '/^#/d' < "${SCRIPT_DIR}/python_modules.txt" | cut -d\| -f1 | tr '\n' ' ' | sed 's# *$##;s# \{1,\}# #g')
echo "Checking python modules installed: $MOD_LIST"
${PYTHON} -c "import importlib.util, sys; sys.exit(0 if all(importlib.util.find_spec(mod.strip()) for mod in '''$MOD_LIST'''.split()) else 1)"
if [ $? -eq 1 ] ; then
echo "At least one of the required python modules is missing"
bash ${SCRIPT_DIR}/install_python_modules.sh
fi
cmake --build .
if [ $? -ne 0 ] ; then
echo "Error running actual cmake build. Aborting"
exit 1
fi
# write it into the dist dir
cmake --build . --target dist
if [ $? -ne 0 ] ; then
echo "Error running cmake distribution. Aborting"
exit 1
fi
echo "Built PC version in build/dist folder"
exit 0
fi
if [ -z "$SETUP_NEW_BOARD" ] ; then
# Did not specify -s flag, so do not overwrite local changes with new board
# but do re-generate the INI file, this ensures upstream changes are pulled into
# existing builds (e.g. upgrading platformio version)
# Check the local ini file has been previously generated as we need to read which board the user is building
if [ ! -f "$LOCAL_INI_VALUES_FILE" ] ; then
echo "ERROR: local platformio ini file not found."
echo "Please see documentation in build-sh.md, and re-run build as follows:"
echo " ./build.sh -s BUILD_BOARD"
echo "BUILD_BOARD values include:"
for f in $(ls -1 build-platforms/platformio-*.ini); do
BASE_NAME=$(basename $f)
BOARD_NAME=$(echo ${BASE_NAME//.ini} | cut -d\- -f2-)
echo " - $BOARD_NAME"
done
echo "This is only required to be done once."
exit 1
fi
if [ ${ZIP_MODE} -eq 1 ] ; then
${PYTHON} create-platformio-ini.py -o $INI_FILE -l $LOCAL_INI_VALUES_FILE -f platformio-ini-files/platformio.zip-options.ini
else
${PYTHON} create-platformio-ini.py -o $INI_FILE -l $LOCAL_INI_VALUES_FILE
fi
create_result=$?
else
# this will create a clean platformio INI file, but honours the command line args
if [ -e ${LOCAL_INI_VALUES_FILE} -a $ANSWER_YES -eq 0 ] ; then
echo "WARNING! This will potentially overwrite any local changes in $LOCAL_INI_VALUES_FILE"
echo -n "Do you want to proceed? (y|N) "
read answer
answer=$(echo $answer | tr '[:upper:]' '[:lower:]')
if [ "$answer" != "y" ]; then
echo "Aborting"
exit 1
fi
fi
if [ ${ZIP_MODE} -eq 1 ] ; then
${PYTHON} create-platformio-ini.py -n $SETUP_NEW_BOARD -o $INI_FILE -l $LOCAL_INI_VALUES_FILE -f platformio-ini-files/platformio.zip-options.ini
else
${PYTHON} create-platformio-ini.py -n $SETUP_NEW_BOARD -o $INI_FILE -l $LOCAL_INI_VALUES_FILE
fi
create_result=$?
fi
if [ $create_result -ne 0 ] ; then
echo "Could not run build due to previous errors. Aborting"
exit $create_result
fi
BUILD_BOARD=$(grep '^build_board = ' $INI_FILE | cut -d" " -f 3)
##############################################################
# ZIP MODE for building firmware zip file.
# This is Separate from the main build, and if chosen exits after running
if [ ${ZIP_MODE} -eq 1 ] ; then
echo "=============================================================="
echo "Running pio tasks: clean, buildfs for env $BUILD_BOARD"
pio run -c $INI_FILE -t clean -t buildfs -e $BUILD_BOARD
if [ $? -ne 0 ]; then
echo "Error building filesystem."
exit 1
fi
echo "=============================================================="
echo "Running main pio build task"
pio run -c $INI_FILE --disable-auto-clean -e $BUILD_BOARD
exit $?
fi
##############################################################
# NORMAL BUILD MODES USING pio
ENV_ARG=""
if [ -n "${ENV_NAME}" ] ; then
ENV_ARG="-e ${ENV_NAME}"
fi
TARGET_ARG=""
if [ -n "${TARGET_NAME}" ] ; then
TARGET_ARG="-t ${TARGET_NAME}"
fi
DEV_MODE_ARG=""
if [ ${DEV_MODE} -eq 1 ] ; then
DEV_MODE_ARG="-a dev"
fi
if [ ${DO_CLEAN} -eq 1 ] ; then
pio run -c $INI_FILE -t clean ${ENV_ARG}
fi
AUTOCLEAN_ARG=""
if [ ${AUTOCLEAN} -eq 0 ] ; then
AUTOCLEAN_ARG="--disable-auto-clean"
fi
# any stage that fails from this point should stop the script immediately, as they are designed to run
# on from each other sequentially as long as the previous passed.
set -e
if [ ${RUN_BUILD} -eq 1 ] ; then
pio run -c $INI_FILE ${DEV_MODE_ARG} $ENV_ARG $TARGET_ARG $AUTOCLEAN_ARG 2>&1
fi
if [ ${UPLOAD_FS} -eq 1 ] ; then
pio run -c $INI_FILE ${DEV_MODE_ARG} -t uploadfs 2>&1
fi
if [ ${UPLOAD_IMAGE} -eq 1 ] ; then
pio run -c $INI_FILE ${DEV_MODE_ARG} -t upload 2>&1
fi
if [ ${SHOW_MONITOR} -eq 1 ] ; then
# device monitor hard codes to using platformio.ini, let's grab all the data it would use directly from our generated ini.
MONITOR_PORT=$(grep ^monitor_port $INI_FILE | cut -d= -f2 | cut -d\; -f1 | awk '{print $1}')
MONITOR_SPEED=$(grep ^monitor_speed $INI_FILE | cut -d= -f2 | cut -d\; -f1 | awk '{print $1}')
MONITOR_FILTERS=$(grep ^monitor_filters $INI_FILE | cut -d= -f2 | cut -d\; -f1 | tr ',' '\n' | sed 's/^ *//; s/ *$//' | awk '{printf("-f %s ", $1)}')
# warn the user if the build_board in platformio.ini (if exists) is not the same as INI_FILE version, as that means stacktrace will not work correctly
# because the monitor does not allow an INI file to be set!!
PIO_BOARD=$(grep "build_board *=" platformio.ini | awk '{print $3}')
INI_BOARD=$(grep "build_board *=" ${INI_FILE} | awk '{print $3}')
if [ "${PIO_BOARD}" != "${INI_BOARD}" ]; then
echo "╔═════════════════════════════════════════╗"
echo "║ WARNING ║"
echo "╟─────────────────────────────────────────╢"
echo "║ INCONSISTENT build_board VALUE DETECTED ║"
echo "║ THIS MEANS STACKTRACE WILL NOT WORK ║"
echo "╚═════════════════════════════════════════╝"
echo ""
echo " platformio.ini = ${PIO_BOARD}"
echo " $(basename ${INI_FILE}) = ${INI_BOARD}"
echo ""
echo " This is because 'pio device monitor' does not allow setting the INI file to use, but 'pio run' does."
echo " You can fix this by copying the build_board to the old platformio.ini, or copy $(basename ${INI_FILE}) over platformio.ini entirely"
echo ""
fi
pio device monitor -p $MONITOR_PORT -b $MONITOR_SPEED $MONITOR_FILTERS 2>&1
fi