diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..30e8ad8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +Dockerfile +LICENSE +README.md diff --git a/Dockerfile b/Dockerfile index 6f27c72..c0ff266 100644 --- a/Dockerfile +++ b/Dockerfile @@ -183,4 +183,7 @@ RUN \ RUN ln -s /usr/bin/gdb-multiarch /usr/bin/arm-none-eabi-gdb RUN echo "${USER_NAME} ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/keil +COPY ./compile.sh /bin/st-compile +RUN chmod a+x /bin/st-compile + ENV USER_NAME=${USER_NAME} diff --git a/compile.sh b/compile.sh new file mode 100644 index 0000000..15a0f61 --- /dev/null +++ b/compile.sh @@ -0,0 +1,232 @@ +#!/bin/bash +projectPattern="*.uvprojx"; + +function handle_status() { + if [ $1 -ne 0 ]; then + echo "Error: $2 (status code: $1)"; + exit $1; + fi +} + +function regex_escape() { + echo "$1" | sed "s/[^\^]/[&]/g; s/\^/\0/g"; +} + +function normalize_path() { + echo "$1" | sed "s/\\\\/\\//g"; +} + +function getTargetXPath() { + index="$1"; + echo -n "Project/Targets/Target"; + + if [ ! -z "$index" ] + then + echo -n "[$index]"; + fi; + + echo ""; +} + +function getFileXPath() { + index="$1"; + echo "$(echo -n "$(getTargetXPath "$index")")/Groups/Group/Files/File/FilePath"; +} + +function getOptionPath() { + index="$1"; + echo "$(echo -n "$(getTargetXPath "$index")")/TargetOption/TargetCommonOption"; +} + +function find_files() { + local -n files=$1; + arguments=("${@:2}"); + readarray -d '' files < <(find "${arguments[@]}" -print0); + return; +} + +function find_project() { + file="$1"; + currentDir="$(dirname "$file")"; + find_files projectFiles "$currentDir" -name "$projectPattern"; + + while true; + do + for projectFile in "${projectFiles[@]}" + do + if containsFile "$file" "$projectFile" + then + echo "$projectFile"; + return; + fi; + done; + + if [ "$currentDir" != "/" ]; + then + currentDir="$(dirname "$currentDir")"; + find_files projectFiles "$currentDir" -maxdepth 1 -name "$projectPattern"; + else + break; + fi; + done; +} + +function containsFile() { + local i; + file="$1"; + projectFile="$2"; + targetIndex="$3"; + xpath="$(getFileXPath "$targetIndex")"; + + relativePath="$(realpath --relative-to "$(dirname "$projectFile")" "$(dirname "$file")")"; + filePattern="$(regex_escape "$(basename "$file")")"; + + while [ "$relativePath" != "." ]; + do + filePattern="$(regex_escape "$(basename "$relativePath")")"'[/\\\\]'"$filePattern"; + relativePath="$(dirname "$relativePath")"; + done; + + fileCount="$(xmlstarlet select --template --copy-of "count($xpath)" "$projectFile")"; + + for i in $(seq 1 $fileCount) + do + currentFileName="$(xmlstarlet select --template --match "($xpath)[$i]" --value-of . "$projectFile")"; + currentFileName="$(normalize_path "$currentFileName")"; + currentFileName="$(realpath --relative-to=. -m "$currentFileName")"; + + if echo "$currentFileName" | grep "^$filePattern$" > /dev/null + then + true; + return; + fi; + done; + + false; +} + +function chooseTarget() { + local i; + projectFile="$1"; + file="$2"; + targetCandidates=(); + targetCount="$(xmlstarlet select --template --copy-of "count($(getTargetXPath))" "$projectFile")"; + + if [ $targetCount -gt 0 ] + then + for i in $(seq 1 $targetCount) + do + if containsFile "$file" "$projectFile" "$i" + then + targetCandidates+=("$i"); + fi; + done; + + if [ "${#targetCandidates[@]}" = 0 ] + then + targetCandidates=($(seq 1 "$targetCount")); + fi; + + while [ ! "${#targetCandidates[@]}" = 1 ] + do + + echo "Which Target Would You Like to Build?"; + + for j in $(seq 1 "${#targetCandidates[@]}") + do + index="${targetCandidates["$(expr $j - 1)"]}"; + targetName="$(xmlstarlet select --template --match "$(getTargetXPath "$index")/TargetName" --value-of . "$projectFile")"; + echo "$(printf "%${#targetCount}s" "$index"): \"$targetName\""; + done; + read -p "Enter your choice: " choice; + + if [ $choice -gt 0 ] && [ $choice -le "${#targetCandidates[@]}" ] + then + targetCandidates=("${targetCandidates[$(expr $choice - 1)]}"); + fi; + done; + + return "${targetCandidates[0]}"; + else + handle_status 1 "The current project '$projectFile' doesn't contain any targets!"; + fi; +} + +fileName="$(realpath "$1")"; + +if [[ "$fileName" =~ \.uvprojx$ ]] +then + projectFile="$fileName"; +else + projectFile="$(find_project "$fileName")"; +fi + +if [ ! -z "$projectFile" ]; +then + projectRoot="$(dirname "$projectFile")"; + cd "$projectRoot"; + tempFile="$(mktemp)"; + cp -f "$projectFile" "$tempFile"; + + chooseTarget "$projectFile" "$fileName"; + targetIndex="$?"; + + targetPath="$(getTargetXPath "$targetIndex")"; + optionPath="$(getOptionPath "$targetIndex")"; + + fileCount="$(xmlstarlet select --template --copy-of "count($(getFileXPath))" "$projectFile")"; + targetName="$(xmlstarlet select --template --match "$targetPath/TargetName" --value-of . "$projectFile")"; + buildPath="$(xmlstarlet select --template --match "$optionPath/OutputDirectory" --value-of . "$projectFile")"; + outputName="$(xmlstarlet select --template --match "$optionPath/OutputName" --value-of . "$projectFile")"; + buildPath="$(normalize_path "$buildPath")"; + outputName="$(normalize_path "$outputName")"; + + echo "Building Target \"$targetName\""; + echo ""; + + for i in $(seq 1 $fileCount) + do + xpath="($(getFileXPath))[$i]" + currentFileName="$(xmlstarlet select --template --match "$xpath" --value-of . "$projectFile")"; + currentFileName="$(normalize_path "$currentFileName")"; + xmlstarlet edit --inplace --update "$xpath" --value "$currentFileName" "$projectFile"; + done; + + logFile="$(mktemp)"; + + xvfb-run bash -c "dwm & wine \"C:\Keil_v5\UV4\UV4.exe\" -j0 -b \"$projectFile\" -t \"$targetName\" -l \"$logFile\"" > /dev/null 2>&1; + mv -f "$tempFile" "$projectFile"; + + errorPattern="\(.*\)\(([[:digit:]]\+): \(warning\|note\|error\): \([A-Z0-9]\+: \)\?.*\)$"; + + sed -i \ + -e "/$errorPattern/{" \ + -e "h; s/$errorPattern/realpath \"\1\"/;" \ + -e "x; s/$errorPattern/\2/; x;" \ + -e "e" \ + -e "G; s/\n//;" \ + -e "}" "$logFile"; + + cat "$logFile"; + + if grep -q " 0 Error(s)" "$logFile"; + then + outputName="$(realpath "$projectRoot/$buildPath/$outputName")"; + outputFile="$outputName.axf"; + echo "The File Has Been Built to \`$outputFile\`."; + pyocd list | { + grep '^No available' && + { + echo "Skpping flash." + } || + { + pyocd flash --target stm32f439xi "$outputFile"; + handle_status $? "Flashing failed"; + }; + }; + else + handle_status 1 "The project could not be built!"; + fi; +else + handle_status 1 "No project file for \`${fileName}\` could be found." +fi;