From f1d27384918a9cd5953816c60736e587a4b3b5c8 Mon Sep 17 00:00:00 2001 From: Manuel Thalmann Date: Mon, 27 Mar 2023 12:24:29 +0200 Subject: [PATCH] Create installer script --- Dockerfile | 183 ++------------------------------------ install-keil.sh | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 177 deletions(-) create mode 100755 install-keil.sh diff --git a/Dockerfile b/Dockerfile index c94a11a..ae2e360 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ ARG USER_NAME=keil FROM scottyhardy/docker-wine:devel-8.4 ARG USER_NAME -ARG KEIL_URL=https://armkeil.blob.core.windows.net/eval/MDK537.EXE -ARG PACK_URL=https://ennis.zhaw.ch/pack/InES.CTBoard14_DFP.4.0.3.pack -ARG DEBUG=0 +ARG KEIL_URL= +ARG PACK_URL= +ARG DEBUG= RUN \ apt-get update \ @@ -27,182 +27,11 @@ RUN \ && rm -rf /var/lib/apt/lists/ RUN pip install pyocd +COPY ./install-keil.sh . RUN \ - installerExtension=".exe"; \ - installer="$(mktemp --suffix="$installerExtension")"; \ - installerName="$(basename "$installer" | sed "s/$installerExtension$//")"; \ - export DISPLAY=:90; \ - mkdir -p --mode=777 /tmp/.X11-unix; \ - # Create job for waiting for Xvfb to start - watchStarting="$(mktemp)"; \ - { 2>&1 inotifywait -e create /tmp/.X11-unix/ > /dev/null; } | { sed -u -e "/^Watches established.\$/e rm \"$watchStarting\""; } & \ - displayResolver="$!"; \ - while [ -f "$watchStarting" ]; do true; done; \ - { \ - # Improve arguments for screenshots - ARGS="$([ $DEBUG -eq 1 ] && echo "-screen 0 640x480x24 -dpi 192" || echo "")"; \ - Xvfb "${DISPLAY}" ${ARGS} & \ - }; \ - displayPID="$!"; \ - rm "$installer"; \ - wget "${KEIL_URL}" --progress=bar:force:noscroll -O "$installer"; \ - [ "$DEBUG" -eq 1 ] && { \ - apt-get update; \ - apt-get install -y \ - build-essential \ - imagemagick; \ - rm -rf /var/lib/apt/lists; \ - TIV="$(mktemp)"; \ - logDir="logs"; \ - screenDir="$logDir/screens"; \ - git clone https://github.com/stefanhaustein/TerminalImageViewer.git && \ - cd TerminalImageViewer/src/main/cpp && \ - make && \ - apt-get remove -y build-essential && \ - apt-get autoremove -y && \ - mv tiv "$TIV" && \ - cd - && \ - rm -rf TerminalImageViewer; \ - mkdir -p "$logDir"; \ - mkdir -p "$screenDir"; \ - } || true; \ - printHeading() { \ - local heading; \ - heading="====================================================="; \ - echo "$heading"; \ - [ -z "$1" ] && cat || echo "$1"; \ - echo "$heading"; \ - }; \ - # Run actual Keil uVision installer - install_keil() { \ - try="$1"; \ - logFile="$(mktemp)"; \ - printHeading "Starting ARM Keil installation"; \ - # Run installer asynchronously - { \ - { \ - local exitCode; \ - exitCodeFile="$(mktemp)"; \ - { \ - 2>&1 WINEDEBUG=+all,trace-all,warn-all /usr/bin/entrypoint wine "$installer" --batch-install; \ - echo "$?" > "$exitCodeFile"; \ - } \ - # Write output to a log file when debugging - | { [ "$DEBUG" -eq 1 ] && tee "$logFile" || cat; } \ - # Pipe stdout and stderr to `handle_output` function - | handle_output; \ - outputHandlerCode="$?"; \ - exitCode="$(cat "$exitCodeFile")"; \ - rm "$exitCodeFile"; \ - bash -c "exit $([ "$outputHandlerCode" -ne 0 ] && echo "$outputHandlerCode" || echo "$exitCode")"; \ - } & \ - }; \ - pid="$!"; \ - # Continuously display screenshots and iteration number - [ "$DEBUG" -eq 1 ] \ - && { \ - { \ - x=0; \ - maxCount=120; \ - while ps -p "$pid" > /dev/null && [ "$x" -ne "$maxCount" ]; do \ - # Create screenshot and print it to the console - fileName="$screenDir/install-try$try-$x.png"; \ - import -window root "$fileName"; \ - "$TIV" -w 10000 -h 10000 "$fileName"; \ - x="$(expr "$x" + 1)"; \ - echo "Frame #$x"; \ - sleep 1; \ - done; \ - # Kill installer after timeout indicated by `maxCount` - [ "$x" -eq "$maxCount" ] && { kill_installer; mv "$logFile" "$logDir/timeout-try$try.log"; }; \ - } & \ - }; \ - wait "$pid"; \ - exitCode="$?"; \ - # Rename log file according to the state of the installer - [ "$DEBUG" -eq 1 ] \ - && [ -f "$logFile" ] \ - && { \ - # Move logs to location based on exit code - [ "$exitCode" -eq 0 ] && mv "$logFile" "$logDir/success-try$try.log"; \ - [ "$exitCode" -eq 42 ] && mv "$logFile" "$logDir/timeout-handled-try$try.log"; \ - [ "$exitCode" -ne 0 ] && mv "$logFile" "$logDir/fail-try$try.log"; \ - }; \ - { \ - [ "$exitCode" -ne 0 ] && { \ - [ "$exitCode" -eq 42 ] && echo "The installation Got Stuck" || \ - echo "The Installation Failed"; \ - } || \ - echo "The Installation Was Successful"; \ - echo "Installation Process Exited With Code $exitCode"; \ - } | printHeading; \ - return "$exitCode"; \ - }; \ - # Handle console output of Keil uVision installer - handle_output() { \ - # A pattern that is supposed to indicates that wine got stuck - pattern="fixme:win:NtUserLockWindowUpdate ((nil))"; \ - # The amount of times the pattern should occur for the process to be considered stuck - patternCount=2; \ - sed \ - -u \ - # Handle multiple occurrences of $pattern - -e "/$pattern/{ x; s/^x\{$(expr $patternCount - 1)\}$/\0/; x; t stuck; x; s/^\(x*\)$/\1x/; x; };" \ - # Return to start - -e "b;" \ - # Branch "stuck": delete output and exit with code 42 - -e ":stuck q42;" \ - # Branch "end": exit with code 0 - -e ":end q;"; \ - exitCode="$?"; \ - # Kill installer if it got stuck (according to console output) - [ "$exitCode" -eq 42 ] && kill_installer; \ - # Use `sed`s exit code if non-zero - [ "$exitCode" -ne 0 ] && exit "$exitCode" || \ - # Return true otherwise - true; \ - }; \ - kill_installer() { \ - pkill -9 "$installerName" > /dev/null 2>&1; \ - }; \ - # Wait for Xvfb to start - wait "$displayResolver"; \ - y=0; \ - while ! \ - install_keil \ - "$y" \ - ; \ - do \ - printHeading "Restarting Installation"; \ - # Increase counter - y="$(expr "$y" + 1)"; \ - done && \ - kill -9 "$displayPID" > /dev/null 2>&1; \ - # Cleanup Unnecessary Files - rm -f "$TIV"; \ - rm -rf "$(/usr/bin/entrypoint winepath 'C:\Keil_v5')/Backup."*; \ - rm "$installer"; \ - rm -f /tmp/.X*-lock; \ - [ "$DEBUG" -eq 1 ] && { \ - apt-get remove -y imagemagick; \ - apt-get autoremove -y; \ - } || true - -RUN \ - keilConfig="$(/usr/bin/entrypoint winepath 'C:\Keil_v5\TOOLS.INI')" && \ - winAppData="$(/usr/bin/entrypoint wine cmd.exe /c 'echo %LOCALAPPDATA%' | tr -d '\r' | sed 's/\\/\\\\/g')" && \ - packDir="$winAppData"'\Arm\Packs' && \ - sed -i '/^RTEPATH=/d' "$keilConfig" && \ - echo 'RTEPATH="'"$packDir"'"' | sed -i '/\[UV2\]/ r /dev/stdin' "$keilConfig" - -RUN \ - wget "${PACK_URL}" --progress=bar:force:noscroll --no-check-certificate -O InES.pack; \ - /usr/bin/entrypoint xvfb-run wine 'C:\Keil_v5\UV4\PackUnzip.exe' --embedded --agree-license InES.pack & \ - pid="$!"; \ - wait "$pid"; \ - rm InES.pack; \ - rm -f /tmp/.X*-lock; + ./install-keil.sh && \ + rm ./install-keil.sh RUN \ apt-get update && \ diff --git a/install-keil.sh b/install-keil.sh new file mode 100755 index 0000000..64ac9c9 --- /dev/null +++ b/install-keil.sh @@ -0,0 +1,230 @@ +#!/bin/bash +export DEBUG="${DEBUG:-0}"; +export KEIL_URL="${KEIL_URL:-https://armkeil.blob.core.windows.net/eval/MDK537.EXE}"; +export PACK_URL="${PACK_URL:-https://ennis.zhaw.ch/pack/InES.CTBoard14_DFP.4.0.3.pack}"; + +installerExtension=".exe"; +installer="$(mktemp --suffix="$installerExtension")"; +installerName="$(basename "$installer" | sed "s/$installerExtension$//")"; + +export DISPLAY=:90; +mkdir -p --mode=777 /tmp/.X11-unix; + +# Create a job for waiting for Xvfb to start +watchStarting="$(mktemp)"; +{ 2>&1 inotifywait -e create /tmp/.X11-unix/; } | { sed -u -e "/^Watches established.$/e rm \"$watchStarting\""; } & +displayResolver="$!"; + +# Wait for `inotifywait` to start +while [ -f "$watchStarting" ]; do sleep 0.5; done; + +{ + # Improve arguments for screenshots + ARGS="$([ "$DEBUG" -eq 1 ] && echo "-screen 0 640x480x24 -dpi 192" || echo "")"; + Xvfb "${DISPLAY}" ${ARGS} & +}; + +displayPID="$!"; +wget ${KEIL_URL} --progress=bar:force:noscroll -O "$installer"; + +# Preparations for Debug Logs +if [ "$DEBUG" -eq 1 ] +then + dir="$(mktemp -d)"; + TIV="$(mktemp)"; + logDir="log"; + screenDir="$logDir/screens"; + git clone https://github.com/stefanhaustein/TerminalImageViewer.git "$dir"; + git checkout "v1.1.1"; + cd "$dir/src/main/cpp"; + make; + mv tiv "$TIV"; + cd -; + rm -rf "$dir"; + mkdir -p "$logDir"; + mkdir -p "$screenDir"; +fi + +function printHeading() { + local heading; + heading="====================================================="; + echo "$heading"; + + if [ -z "$1" ] + then + cat; + else + echo "$1"; + fi; + + echo "$heading"; +} + +function kill_installer() { + pkill -9 "$installerName" > /dev/null 2>&1; +} + +function handle_output() { + # Pattern that is supposed to indicate that wine got stuck + pattern="fixme:win:NtUserLockWindowUpdate ((nil))"; + # The number of occurences of the pattern which have to occur to assume wine got stuck + patternCount=2; + + sed \ + -u \ + `# Handle the pattern and the chosen pattern count` \ + -e "/$pattern/{ x; s/^x\{$(expr "$patternCount" - 1)\}$/\0/; x; t stuck; x; s/^\(x*\)$/\1x/; x; };" \ + `# Return to start` \ + -e "b;" \ + `# Branch "stuck": delete output and exit with code 42` \ + -e ":stuck q42;" \ + `# Branch "end": exit with code 0` \ + -e ":end q;"; + + exitCode="$?"; + + # Kill installer if it got stuck (based on the console output and the pattern) + if [ "$exitCode" -ne 0 ] + then + kill_installer; + return "$exitCode"; + fi; + + return 0; +} + +# Install Keil uVision +function install_keil() { + try="$1"; + logFile="$(mktemp)"; + printHeading "Starting ARM Keil Installation"; + + # Run installer asynchronously + { + { + local exitCode; + exitCodeFile="$(mktemp)"; + + { + 2>&1 WINEDEBUG=+all,trace-all,warn-all wine "$installer" --batch-install; + echo "$?" > "$exitCodeFile"; + } | \ + # Write output to a log file when debugging + { [ "$DEBUG" -eq 1 ] && { tee "$logFile"; } || cat; } | \ + # Pipe stdout and stderr to `handle_output` function + handle_output; + + outputHandlerCode="$?"; + exitCode="$(cat "$exitCodeFile")"; + rm "$exitCodeFile"; + + if [ "$outputHandlerCode" -ne 0 ] + then + exitCode="$outputHandlerCode"; + fi; + + bash -c "exit $exitCode"; + } & + }; + + pid="$!"; + + # Continuously display screenshots and iteration number when debugging + if [ "$DEBUG" -eq 1 ] + then + { + { + x=0; + maxCount=120; + + while ps -p "$pid" > /dev/null && [ "$x" -ne "$maxCount" ] + do + # Create screenshot and print it to the console + fileName="$screenDir/install-try$try-$x.png"; + import -window root "$fileName"; + "$TIV" -w 10000 -h 10000 "$fileName"; + x="$(expr "$x" + 1)"; + echo "Frame #$x"; + sleep 1; + done; + + # Kill the installer once `maxCount` has been reached + if [ "$x" -eq "$maxCount" ] + then + kill_installer; + mv "$logFile" "./$logDir/log-timeout-try$try.log"; + fi; + } & + }; + fi; + + wait "$pid"; + exitCode="$?"; + + if [ "$DEBUG" -eq 1 ] && [ -f "$logFile" ] + then + if [ $exitCode -eq 0 ] + then + mv "$logFile" "./$logDir/success-try$try.log"; + elif [ $exitCode -eq 42 ] + then + mv "$logFile" "./$logDir/timeout-handled-try$try.log"; + else + mv "$logFile" "./$logDir§/fail-try$try.txt"; + fi; + fi; + + { + if [ $exitCode -ne 0 ] + then + if [ $exitCode -eq 42 ] + then + echo "The Installation Got Stuck"; + else + echo "The Installation Failed"; + fi; + else + echo "The Installation Was Successul"; + fi; + + echo "The Installation Process Exited With Code $exitCode"; + } | printHeading; + + return "$exitCode"; +} + +# Wait for Xvfb to start +wait "$displayResolver"; + +y=0; + +while ! install_keil "$y" +do + printHeading "Restarting Installation"; + y="$(expr "$y" + 1)"; +done; + +kill -9 "$displayPID" > /dev/null 2>&1; + +# Delete unfinished installations +rm -rf "$(winepath 'C:\Keil_v5')/Backup."*; +rm "$installer"; +rm -rf /tmp/.X*-lock; + +# Fix incorrect `RTEPATH` setting +keilConfig="$(winepath 'C:\Keil_v5\TOOLS.INI')"; +winAppData="$(wine cmd.exe /c 'echo %LOCALAPPDATA%' | tr -d '\r' | sed 's/\\/\\\\/g')"; +packDir="$winAppData"'\Arm\Packs'; +sed -i '/^RTEPATH=/d' "$keilConfig"; +echo 'RTEPATH="'"$packDir"'"' | sed -i '/\[UV2\]/ r /dev/stdin' "$keilConfig"; + +# Install additional package +if [ ! -z "$PACK_URL" ] +then + packFile="$(mktemp)"; + wget "${PACK_URL}" --progress=bar:force:noscroll --no-check-certificate -O "$packFile"; + xvfb-run wine "C:\Keil_v5\UV4\PackUnzip.exe" --embedded --agree-license "$packFile" & + pid="$!"; \ + wait "$pid"; \ + rm "$packFile"; +fi;