Fix Studuino setup reproducibility
This commit is contained in:
@@ -6,40 +6,35 @@ set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ARDUINO_USER_OVERRIDE="${ARDUINO_USER:-}"
|
||||
ARDUINO_USER="${ARDUINO_USER:-$HOME/Arduino}"
|
||||
RESOURCE_DIR="$SCRIPT_DIR/Resources"
|
||||
LIBRARY_ARCHIVE="$RESOURCE_DIR/studuino_v1_lib.zip"
|
||||
HARDWARE_ARCHIVE="$RESOURCE_DIR/arduino_IDE_hardware_settings_ver17x.zip"
|
||||
HARDWARE_SOURCE_DIR="$SCRIPT_DIR/hardware/studuino/avr"
|
||||
LIBRARY_SOURCE_DIR="$SCRIPT_DIR/libraries"
|
||||
FQBN="studuino:avr:studuino"
|
||||
MIN_AVR_CORE_VERSION="1.8.8"
|
||||
BUILD_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/arduino/studuino-compile-db"
|
||||
VERIFY_BUILD_DIR="$BUILD_DIR/verify"
|
||||
SMOKE_VERIFY_BUILD_DIR="$BUILD_DIR/verify-studuino-blink"
|
||||
SMOKE_SKETCH_DIR="$LIBRARY_SOURCE_DIR/Studuino/example/Blink"
|
||||
|
||||
FORCE=0
|
||||
CLEAN_LIBRARIES=0
|
||||
SKIP_PACKAGES=0
|
||||
SKIP_PERMISSIONS=0
|
||||
SKIP_COMPILE_DB=0
|
||||
WITH_EDITOR=0
|
||||
WITH_MONITOR=0
|
||||
CONFIG_FILE="${ARDUINO_CONFIG_FILE:-}"
|
||||
TEMP_DIRS=()
|
||||
|
||||
cleanup() {
|
||||
local temporary
|
||||
for temporary in "${TEMP_DIRS[@]}"; do
|
||||
[[ -d "$temporary" ]] && rm -rf -- "$temporary"
|
||||
done
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: ./setup.sh [options]
|
||||
|
||||
Options:
|
||||
--force Replace existing Studuino library/hardware files.
|
||||
--clean Remove user libraries not supplied by the Studuino bundle.
|
||||
--force Replace hardware/libraries with checked-in versions.
|
||||
--clean Remove user libraries absent from this repository.
|
||||
--skip-packages Do not install Arch packages.
|
||||
--skip-permissions Do not modify serial-port group membership.
|
||||
--skip-compile-db Do not generate compile_commands.json.
|
||||
--with-editor Install Neovim, clang, and Arduino language-server tools.
|
||||
--with-monitor Also install picocom for a serial monitor.
|
||||
-h, --help Show this help.
|
||||
|
||||
@@ -66,6 +61,9 @@ while (($# > 0)); do
|
||||
--skip-compile-db)
|
||||
SKIP_COMPILE_DB=1
|
||||
;;
|
||||
--with-editor)
|
||||
WITH_EDITOR=1
|
||||
;;
|
||||
--with-monitor)
|
||||
WITH_MONITOR=1
|
||||
;;
|
||||
@@ -129,24 +127,28 @@ install_packages() {
|
||||
info "Skipping Arch package installation."
|
||||
else
|
||||
require_command pacman
|
||||
local -a packages=(
|
||||
arduino-cli
|
||||
clangd
|
||||
make
|
||||
neovim
|
||||
python
|
||||
unzip
|
||||
)
|
||||
local -a packages=(arduino-cli)
|
||||
|
||||
if ((WITH_EDITOR)); then
|
||||
packages+=(clang neovim arduino-language-server)
|
||||
fi
|
||||
if ((WITH_MONITOR)); then
|
||||
packages+=(picocom)
|
||||
fi
|
||||
|
||||
info "Installing required Arch packages."
|
||||
as_root pacman -S --needed "${packages[@]}"
|
||||
fi
|
||||
|
||||
require_command arduino-cli
|
||||
require_command python3
|
||||
require_command unzip
|
||||
if ((WITH_EDITOR)); then
|
||||
require_command clangd
|
||||
require_command nvim
|
||||
require_command arduino-language-server
|
||||
fi
|
||||
if ((WITH_MONITOR)); then
|
||||
require_command picocom
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_serial_permissions() {
|
||||
@@ -207,7 +209,29 @@ resolve_arduino_user() {
|
||||
fi
|
||||
}
|
||||
|
||||
validate_hardware_files() {
|
||||
local directory="$1"
|
||||
|
||||
[[ -f "$directory/boards.txt" ]] || die "Missing Studuino board definition: $directory/boards.txt"
|
||||
[[ -f "$directory/platform.txt" ]] || die "Missing Studuino platform definition: $directory/platform.txt"
|
||||
[[ -f "$directory/platform.local.txt" ]] || die "Missing Studuino CLI overrides: $directory/platform.local.txt"
|
||||
grep -q '^studuino.name=' "$directory/boards.txt" \
|
||||
|| die "The Studuino board definition is missing its board entry."
|
||||
grep -q '^name=' "$directory/platform.txt" \
|
||||
|| die "The Studuino platform definition is missing its platform name."
|
||||
}
|
||||
|
||||
hardware_matches_source() {
|
||||
local file
|
||||
for file in boards.txt platform.txt platform.local.txt; do
|
||||
cmp -s -- "$HARDWARE_SOURCE_DIR/$file" "$ARDUINO_USER/hardware/studuino/avr/$file" || return 1
|
||||
done
|
||||
}
|
||||
|
||||
ensure_hardware_layout() {
|
||||
[[ -d "$HARDWARE_SOURCE_DIR" ]] || die "Checked-in hardware directory not found: $HARDWARE_SOURCE_DIR"
|
||||
validate_hardware_files "$HARDWARE_SOURCE_DIR"
|
||||
|
||||
local vendor_dir="$ARDUINO_USER/hardware/studuino"
|
||||
local hardware_dir="$vendor_dir/avr"
|
||||
local nested_dir="$vendor_dir/studuino/avr"
|
||||
@@ -227,120 +251,88 @@ ensure_hardware_layout() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$hardware_dir/boards.txt" && -f "$hardware_dir/platform.txt" ]] && (( FORCE == 0 )); then
|
||||
info "Studuino hardware files are already installed; keeping them."
|
||||
if [[ "$HARDWARE_SOURCE_DIR" -ef "$hardware_dir" ]]; then
|
||||
info "Using the checked-in Studuino hardware definition."
|
||||
elif [[ -L "$hardware_dir" ]]; then
|
||||
die "Refusing to replace symlinked hardware directory: $hardware_dir"
|
||||
elif [[ -e "$hardware_dir" ]] && ((FORCE == 0)); then
|
||||
if hardware_matches_source; then
|
||||
info "Studuino hardware files are already installed; keeping them."
|
||||
else
|
||||
warn "Installed Studuino hardware differs from this repository; keeping it. Re-run with --force to replace it."
|
||||
fi
|
||||
else
|
||||
[[ -f "$HARDWARE_ARCHIVE" ]] || die "Hardware archive not found: $HARDWARE_ARCHIVE"
|
||||
mkdir -p -- "$hardware_dir"
|
||||
|
||||
# The official archive was created on Windows and uses backslashes in ZIP
|
||||
# entry names. Extract only the Linux/other Studuino platform files.
|
||||
python3 - "$HARDWARE_ARCHIVE" "$hardware_dir" "$FORCE" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from zipfile import ZipFile
|
||||
|
||||
archive = Path(sys.argv[1])
|
||||
destination = Path(sys.argv[2])
|
||||
force = sys.argv[3] == "1"
|
||||
|
||||
wanted = {
|
||||
"arduino_ide_settings_ver17x/other/hardware/studuino/avr/boards.txt": "boards.txt",
|
||||
"arduino_ide_settings_ver17x/other/hardware/studuino/avr/platform.txt": "platform.txt",
|
||||
}
|
||||
found = set()
|
||||
|
||||
with ZipFile(archive) as zipped:
|
||||
for info in zipped.infolist():
|
||||
normalized = info.filename.replace("\\", "/")
|
||||
target_name = wanted.get(normalized)
|
||||
if target_name is None:
|
||||
continue
|
||||
target = destination / target_name
|
||||
if target.exists() and not force:
|
||||
print(f"[studuino] Keeping existing {target}")
|
||||
else:
|
||||
target.write_bytes(zipped.read(info))
|
||||
print(f"[studuino] Installed {target}")
|
||||
found.add(target_name)
|
||||
|
||||
missing = set(wanted.values()) - found
|
||||
if missing:
|
||||
raise SystemExit("Missing hardware files in archive: " + ", ".join(sorted(missing)))
|
||||
PY
|
||||
if ((FORCE)) && [[ -e "$hardware_dir" ]]; then
|
||||
rm -rf -- "$hardware_dir"
|
||||
mkdir -p -- "$hardware_dir"
|
||||
fi
|
||||
cp -a -- "$HARDWARE_SOURCE_DIR/." "$hardware_dir/"
|
||||
info "Installed checked-in Studuino hardware files: $hardware_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
# The archive targets the legacy IDE's tool paths. These overrides make it
|
||||
# work with the AVR tools installed by arduino-cli.
|
||||
local local_properties="$hardware_dir/platform.local.txt"
|
||||
cat >"$local_properties" <<'EOF'
|
||||
# Generated by setup.sh for modern arduino-cli AVR tool paths.
|
||||
compiler.path={runtime.tools.avr-gcc.path}/bin/
|
||||
archive_file_path={build.path}/{archive_file}
|
||||
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
|
||||
tools.avrdude.path={runtime.tools.avrdude.path}
|
||||
tools.avrdude.cmd.path={path}/bin/avrdude
|
||||
tools.avrdude.config.path={path}/etc/avrdude.conf
|
||||
EOF
|
||||
info "Wrote modern CLI compatibility overrides: $local_properties"
|
||||
version_at_least() {
|
||||
local current="$1"
|
||||
local minimum="$2"
|
||||
|
||||
grep -q '^studuino.name=' "$hardware_dir/boards.txt" \
|
||||
|| die "The installed Studuino boards.txt is missing its board definition."
|
||||
grep -q '^name=' "$hardware_dir/platform.txt" \
|
||||
|| die "The installed Studuino platform.txt is missing its platform name."
|
||||
[[ "$current" == "$minimum" ]] && return 0
|
||||
[[ "$(printf '%s\n%s\n' "$minimum" "$current" | sort -V | head -n 1)" == "$minimum" ]]
|
||||
}
|
||||
|
||||
ensure_avr_core() {
|
||||
if run_cli core list | awk '$1 == "arduino:avr" { found = 1 } END { exit !found }'; then
|
||||
info "AVR core is already installed."
|
||||
local installed
|
||||
installed="$(run_cli core list | awk '$1 == "arduino:avr" { print $2; exit }')"
|
||||
|
||||
if [[ -z "$installed" ]]; then
|
||||
info "Installing Arduino AVR core $MIN_AVR_CORE_VERSION."
|
||||
run_cli core update-index
|
||||
run_cli core install "arduino:avr@$MIN_AVR_CORE_VERSION"
|
||||
return
|
||||
fi
|
||||
|
||||
info "Installing the Arduino AVR core."
|
||||
if version_at_least "$installed" "$MIN_AVR_CORE_VERSION"; then
|
||||
info "Arduino AVR core $installed is installed."
|
||||
return
|
||||
fi
|
||||
|
||||
warn "Arduino AVR core $installed is older than $MIN_AVR_CORE_VERSION; upgrading."
|
||||
run_cli core update-index
|
||||
run_cli core install arduino:avr
|
||||
run_cli core install "arduino:avr@$MIN_AVR_CORE_VERSION"
|
||||
}
|
||||
|
||||
install_studuino_libraries() {
|
||||
[[ -f "$LIBRARY_ARCHIVE" ]] || die "Library archive not found: $LIBRARY_ARCHIVE"
|
||||
|
||||
local temporary
|
||||
temporary="$(mktemp -d)"
|
||||
TEMP_DIRS+=("$temporary")
|
||||
|
||||
unzip -q "$LIBRARY_ARCHIVE" -d "$temporary"
|
||||
local bundle_dir="$temporary/studuino_v1_lib"
|
||||
[[ -d "$bundle_dir/Studuino" ]] || die "Unexpected Studuino library archive layout."
|
||||
[[ -d "$LIBRARY_SOURCE_DIR/Studuino" ]] \
|
||||
|| die "Checked-in Studuino library not found: $LIBRARY_SOURCE_DIR/Studuino"
|
||||
|
||||
mkdir -p -- "$ARDUINO_USER/libraries"
|
||||
local source_dir destination name
|
||||
for source_dir in "$bundle_dir"/*; do
|
||||
[[ -d "$source_dir" ]] || continue
|
||||
for source_dir in "$LIBRARY_SOURCE_DIR"/*; do
|
||||
[[ -d "$source_dir" && ! -L "$source_dir" ]] || continue
|
||||
name="${source_dir##*/}"
|
||||
destination="$ARDUINO_USER/libraries/$name"
|
||||
|
||||
if [[ -L "$destination" ]]; then
|
||||
if [[ "$source_dir" -ef "$destination" ]]; then
|
||||
info "Using checked-in library: $name"
|
||||
elif [[ -L "$destination" ]]; then
|
||||
die "Refusing to replace symlinked library: $destination"
|
||||
fi
|
||||
if [[ -e "$destination" ]] && ((FORCE == 0)); then
|
||||
elif [[ -e "$destination" ]] && ((FORCE == 0)); then
|
||||
info "Keeping existing Studuino library: $destination"
|
||||
else
|
||||
if [[ -e "$destination" ]]; then
|
||||
rm -rf -- "$destination"
|
||||
fi
|
||||
cp -a -- "$source_dir" "$destination"
|
||||
info "Installed Studuino library: $destination"
|
||||
info "Installed checked-in Studuino library: $destination"
|
||||
fi
|
||||
done
|
||||
|
||||
if ((CLEAN_LIBRARIES)); then
|
||||
local -A keep=()
|
||||
for source_dir in "$bundle_dir"/*; do
|
||||
[[ -d "$source_dir" ]] || continue
|
||||
for source_dir in "$LIBRARY_SOURCE_DIR"/*; do
|
||||
[[ -d "$source_dir" && ! -L "$source_dir" ]] || continue
|
||||
keep["${source_dir##*/}"]=1
|
||||
done
|
||||
# Servo is required by Studuino even though it is not in the ZIP.
|
||||
keep[Servo]=1
|
||||
|
||||
local installed_dir installed_name
|
||||
for installed_dir in "$ARDUINO_USER/libraries"/*; do
|
||||
@@ -348,7 +340,7 @@ install_studuino_libraries() {
|
||||
installed_name="${installed_dir##*/}"
|
||||
if [[ -z "${keep[$installed_name]+present}" ]]; then
|
||||
rm -rf -- "$installed_dir"
|
||||
info "Removed non-Studuino library: $installed_dir"
|
||||
info "Removed library not supplied by this repository: $installed_dir"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -391,17 +383,23 @@ EOF
|
||||
mv -- "$SCRIPT_DIR/sketch.ino" "$sketch"
|
||||
else
|
||||
cat >"$sketch" <<'EOF'
|
||||
#include <Arduino.h>
|
||||
#include <Studuino.h>
|
||||
#include <Clock.h> // Keep the complete smoke-test dependency set in clangd's database.
|
||||
|
||||
Studuino board;
|
||||
|
||||
void setup() {
|
||||
// Add Studuino initialization here.
|
||||
board.InitSensorPort(PORT_A0, PIDLED);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Add Studuino code here.
|
||||
board.LED(PORT_A0, ON);
|
||||
board.Timer(1);
|
||||
board.LED(PORT_A0, OFF);
|
||||
board.Timer(1);
|
||||
}
|
||||
EOF
|
||||
info "Created a minimal sketch at $sketch"
|
||||
info "Created a Studuino smoke-test sketch at $sketch"
|
||||
fi
|
||||
elif [[ -f "$SCRIPT_DIR/sketch.ino" ]]; then
|
||||
warn "Both Arduino.ino and sketch.ino exist; leaving sketch.ino untouched."
|
||||
@@ -416,13 +414,13 @@ generate_compile_database() {
|
||||
|
||||
mkdir -p -- "$BUILD_DIR"
|
||||
info "Generating the Neovim/clangd compilation database."
|
||||
if run_cli compile --clean --fqbn "$FQBN" --only-compilation-database \
|
||||
if ! run_cli compile --clean --fqbn "$FQBN" --only-compilation-database \
|
||||
--build-path "$BUILD_DIR" "$SCRIPT_DIR"; then
|
||||
cp -- "$BUILD_DIR/compile_commands.json" "$SCRIPT_DIR/compile_commands.json"
|
||||
info "Wrote $SCRIPT_DIR/compile_commands.json"
|
||||
else
|
||||
warn "Could not generate compile_commands.json; the Studuino setup is otherwise complete."
|
||||
die "Could not generate compile_commands.json."
|
||||
fi
|
||||
|
||||
cp -- "$BUILD_DIR/compile_commands.json" "$SCRIPT_DIR/compile_commands.json"
|
||||
info "Wrote $SCRIPT_DIR/compile_commands.json"
|
||||
}
|
||||
|
||||
verify_setup() {
|
||||
@@ -436,6 +434,12 @@ verify_setup() {
|
||||
run_cli lib list | grep -E '^Servo[[:space:]]' >/dev/null \
|
||||
|| die "The Servo library is not visible to arduino-cli."
|
||||
|
||||
[[ -f "$SMOKE_SKETCH_DIR/Blink.ino" ]] \
|
||||
|| die "Studuino smoke-test sketch not found: $SMOKE_SKETCH_DIR/Blink.ino"
|
||||
info "Compiling the Studuino Blink smoke test for $FQBN..."
|
||||
run_cli compile --clean --fqbn "$FQBN" \
|
||||
--build-path "$SMOKE_VERIFY_BUILD_DIR" "$SMOKE_SKETCH_DIR"
|
||||
|
||||
info "Compiling the project for $FQBN..."
|
||||
run_cli compile --clean --fqbn "$FQBN" \
|
||||
--build-path "$VERIFY_BUILD_DIR" "$SCRIPT_DIR"
|
||||
|
||||
Reference in New Issue
Block a user