#!/usr/bin/env bash
function interactive_config_prepare_terminal() {
if [[ -z $ROOT_FS_CREATE_ONLY ]]; then
if [[ -t 0 ]]; then
[[ -n $COLUMNS ]] && stty cols $COLUMNS
[[ -n $LINES ]] && stty rows $LINES
declare -g TTY_X=$(($(stty size | awk '{print $2}') - 6))
declare -g TTY_Y=$(($(stty size | awk '{print $1}') - 6))
fi
fi
declare current_year
current_year=$(date +%Y)
declare -g -r backtitle="Armbian Linux build framework, https://www.armbian.com | (c) 2013-${current_year} Igor Pecovnik "
declare -A -g ARMBIAN_INTERACTIVE_CONFIGS=()
}
function set_interactive_config_value() {
declare -g "${1}=${2}"
ARMBIAN_INTERACTIVE_CONFIGS["${1}"]="${2}"
}
function interactive_finish() {
unset TTY_X
unset TTY_Y
unset options
unset DIALOG_EXIT_CODE
unset WIP_STATE
unset SHOW_WARNING
}
function interactive_config_ask_kernel() {
interactive_config_ask_kernel_configure
}
function interactive_config_ask_kernel_configure() {
[[ -n ${KERNEL_CONFIGURE} ]] && return 0
options+=("no" "Do not change the kernel configuration")
options+=("yes" "Show a kernel configuration menu before compilation")
dialog_if_terminal_set_vars --title "Choose an option" --backtitle "$backtitle" --no-tags --menu "Select the kernel configuration" $TTY_Y $TTY_X $((TTY_Y - 8)) "${options[@]}"
set_interactive_config_value KERNEL_CONFIGURE "${DIALOG_RESULT}"
[[ ${DIALOG_EXIT_CODE} != 0 ]] && exit_with_error "You cancelled interactive during kernel configuration" "Build cancelled"
unset options
}
function get_list_of_all_buildable_boards() {
display_alert "Generating list of all available boards" "might take a while" ""
local -a board_types=("conf")
[[ "${WIP_STATE}" != "supported" ]] && board_types+=("wip" "csc" "eos" "tvb")
local -a board_file_paths=("${SRC}/config/boards" "${USERPATCHES_PATH}/config/boards")
local -n ref_arr_all_board_names="${1}"
[[ "${2}" != "" ]] && local -n ref_arr_all_board_options="${2}"
local -n ref_dict_all_board_types="${3}"
local -n ref_dict_all_board_source_files="${4}"
[[ "${5}" != "" ]] && local -n ref_dict_all_board_descriptions="${5}"
declare -i prepare_options=0
if [[ "${2}" != "" || "${5}" != "" ]]; then
prepare_options=1
fi
local board_file_path board_type full_board_file board_name board_desc first_line
for board_file_path in "${board_file_paths[@]}"; do
[[ ! -d "${board_file_path}" ]] && continue
for board_type in "${board_types[@]}"; do
for full_board_file in "${board_file_path}"/*."${board_type}"; do
[[ "${full_board_file}" == *"*"* ]] && continue
board_name="${full_board_file##*/}"
board_name="${board_name%.*}"
ref_dict_all_board_types["${board_name}"]="${board_type}"
ref_dict_all_board_source_files["${board_name}"]="${ref_dict_all_board_source_files["${board_name}"]} ${full_board_file}"
if [[ ${prepare_options} -gt 0 ]]; then
IFS= read -r first_line < "${full_board_file}" || true
board_desc="${first_line#*#}"
board_desc="${board_desc%%#*}"
ref_dict_all_board_descriptions["${board_name}"]="${board_desc}"
fi
done
done
done
if [[ ${prepare_options} -gt 0 ]]; then
declare ref_arr_all_board_names_unsorted=("${!ref_dict_all_board_types[@]}")
declare -a ref_arr_all_board_names_sorted=()
readarray -t ref_arr_all_board_names_sorted < <(printf '%s\n' "${ref_arr_all_board_names_unsorted[@]}" | sort -h)
ref_arr_all_board_names=("${ref_arr_all_board_names_sorted[@]}")
for board_name in "${ref_arr_all_board_names[@]}"; do
ref_arr_all_board_options+=("${board_name}" "\Z1(${ref_dict_all_board_types["${board_name}"]})\Zn ${ref_dict_all_board_descriptions["${board_name}"]}")
done
else
ref_arr_all_board_names=("${!ref_dict_all_board_types[@]}")
fi
return 0
}
function interactive_config_ask_board_list() {
[[ -n ${BOARD} ]] && return 0
declare WIP_STATE=supported
if [[ "${EXPERT}" == "yes" ]]; then
display_alert "Expert mode!" "You can select all boards" "info"
WIP_STATE=unsupported
fi
declare WIP_BUTTON='CSC/WIP/EOS/TVB'
declare STATE_DESCRIPTION=' - boards with high level of software maturity'
declare BOARD_FILTER=''
declare FILTER_TAG='[set filter]'
declare CLEAR_TAG='[clear filter]'
declare temp_rc
temp_rc=$(mktemp)
declare -a arr_all_board_names=() arr_all_board_options=()
declare -A dict_all_board_types=() dict_all_board_source_files=() dict_all_board_descriptions=()
declare board_list_needs_refresh=yes
while true; do
if [[ "${board_list_needs_refresh}" == "yes" ]]; then
arr_all_board_names=() arr_all_board_options=()
dict_all_board_types=() dict_all_board_source_files=() dict_all_board_descriptions=()
get_list_of_all_buildable_boards arr_all_board_names arr_all_board_options dict_all_board_types dict_all_board_source_files dict_all_board_descriptions
board_list_needs_refresh=no
fi
declare -a arr_menu_options=()
if [[ -n "${BOARD_FILTER}" ]]; then
declare lc_filter="${BOARD_FILTER,,}"
declare i tag item hay
declare -a arr_filter_hits=()
for ((i = 0; i < ${#arr_all_board_options[@]}; i += 2)); do
tag="${arr_all_board_options[i]}"
item="${arr_all_board_options[i + 1]}"
hay="${tag} ${dict_all_board_descriptions["${tag}"]}"
hay="${hay,,}"
if [[ "${hay}" == *"${lc_filter}"* ]]; then
arr_filter_hits+=("${tag}" "${item}")
fi
done
declare match_count=$((${#arr_filter_hits[@]} / 2))
arr_menu_options=(
"${FILTER_TAG}" "\Z1change filter (current: ${BOARD_FILTER}, ${match_count}/${#arr_all_board_names[@]} boards)\Zn"
"${CLEAR_TAG}" "\Z1clear filter, show all ${#arr_all_board_names[@]} boards\Zn"
"${arr_filter_hits[@]}"
)
else
arr_menu_options=(
"${FILTER_TAG}" "\Z1filter boards by substring of name or description\Zn"
"${arr_all_board_options[@]}"
)
fi
echo > "${temp_rc}"
if [[ $WIP_STATE != supported ]]; then
cat <<- 'EOF' > "${temp_rc}"
dialog_color = (RED,WHITE,OFF)
screen_color = (WHITE,RED,ON)
tag_color = (RED,WHITE,ON)
item_selected_color = (WHITE,RED,ON)
tag_selected_color = (WHITE,RED,ON)
tag_key_selected_color = (WHITE,RED,ON)
EOF
fi
DIALOGRC=$temp_rc \
dialog_if_terminal_set_vars --title "Choose a board" --backtitle "$backtitle" --scrollbar \
--colors --extra-label "Show $WIP_BUTTON" --extra-button \
--menu "Select the target board. Displaying:\n$STATE_DESCRIPTION" $TTY_Y $TTY_X $((TTY_Y - 8)) "${arr_menu_options[@]}"
declare STATUS=${DIALOG_EXIT_CODE}
declare RESULT="${DIALOG_RESULT}"
if [[ $STATUS == 3 ]]; then
if [[ $WIP_STATE == supported ]]; then
[[ $SHOW_WARNING == yes ]] && show_developer_warning
STATE_DESCRIPTION=' - \Z1(conf)\Zn - Boards with high level of software maturity
\n - \Z1(CSC)\Zn - Community Supported Configuration\n - \Z1(WIP)\Zn - Work In Progress
\n - \Z1(EOS)\Zn - End Of Support\n - \Z1(TVB)\Zn - TV boxes'
WIP_STATE=unsupported
WIP_BUTTON='matured'
EXPERT=yes
else
STATE_DESCRIPTION=' - boards with high level of software maturity'
WIP_STATE=supported
WIP_BUTTON='CSC/WIP/EOS'
EXPERT=no
fi
board_list_needs_refresh=yes
continue
elif [[ $STATUS == 0 ]]; then
if [[ "${RESULT}" == "${FILTER_TAG}" ]]; then
dialog_if_terminal_set_vars --title "Filter boards" --backtitle "$backtitle" \
--inputbox "Case-insensitive substring of board name or description. Empty value clears." 8 "${TTY_X}" "${BOARD_FILTER}"
[[ ${DIALOG_EXIT_CODE} == 0 ]] && BOARD_FILTER="${DIALOG_RESULT}"
continue
elif [[ "${RESULT}" == "${CLEAR_TAG}" ]]; then
BOARD_FILTER=''
continue
fi
set_interactive_config_value BOARD "${RESULT}"
break
else
exit_with_error "You cancelled interactive config" "Build cancelled, board not chosen"
fi
done
}
function get_kernel_info_for_branch() {
local search_branch="$1"
local conf_file="${SRC}/config/sources/families/${BOARDFAMILY}.conf"
awk -v branch="$search_branch" '
BEGIN { found=0; major_minor=""; desc="" }
/^[[:space:]]*[a-zA-Z0-9_*?-][a-zA-Z0-9_*? |-]*\)/ {
label = $0
sub(/\).*/, "", label)
sub(/^[[:space:]]+/, "", label)
sub(/[[:space:]]+$/, "", label)
n = split(label, alts, /[[:space:]]*\|[[:space:]]*/)
matched = 0
for (i = 1; i <= n; i++) {
if (alts[i] == branch) { matched = 1; break }
}
if (matched) {
found = 1
next
} else if (found) {
exit
}
}
found && /declare[[:space:]]+-g[[:space:]]+KERNEL_MAJOR_MINOR=/ {
if (match($0, /"([^"]+)"/, arr)) {
major_minor=arr[1]
}
}
found && /declare[[:space:]]+-g[[:space:]]+KERNEL_DESCRIPTION=/ {
if (match($0, /"([^"]+)"/, arr)) {
desc=arr[1]
}
}
END {
print major_minor "|" desc
}
' "$conf_file"
}
function interactive_config_ask_branch() {
if [[ -n $BRANCH ]]; then
display_alert "Already set BRANCH, skipping interactive" "${BRANCH}" "debug"
return 0
fi
declare -a options=()
declare one_kernel_target
for one_kernel_target in ${KERNEL_TARGET//,/ }; do
local version=""
local description=""
local info
info="$(get_kernel_info_for_branch "$one_kernel_target")"
version="${info%%|*}"
description="${info#*|}"
if [[ -z "$description" ]]; then
case "$one_kernel_target" in
current)
description="Recommended. Usually an LTS kernel"
;;
legacy)
description="Old stable / Legacy kernel"
;;
edge)
description="Bleeding edge / latest possible"
;;
cloud)
description="Cloud optimized minimal LTS kernel"
;;
vendor*)
description="Vendor BSP kernel"
;;
*)
description="Experimental ${one_kernel_target} kernel / for Developers"
;;
esac
fi
if [[ -n "$version" ]]; then
description="${description} (${version})"
fi
options+=("${one_kernel_target}" "${description}")
done
dialog_if_terminal_set_vars --title "Choose a kernel" --backtitle "$backtitle" --colors \
--menu "Select the target kernel branch.\nSelected BOARD='${BOARD}'\nExact kernel versions depend on selected board and its family." \
$TTY_Y $TTY_X $((TTY_Y - 8)) "${options[@]}"
set_interactive_config_value BRANCH "${DIALOG_RESULT}"
[[ -z ${BRANCH} ]] && exit_with_error "No kernel branch selected"
return 0
}
function interactive_config_ask_release() {
[[ -n ${RELEASE} ]] && return 0
declare -a options=()
distros_options
dialog_if_terminal_set_vars --title "Choose a release package base" --backtitle "$backtitle" --menu "Select the target OS release package base; selected BRANCH='${BRANCH}'" $TTY_Y $TTY_X $((TTY_Y - 8)) "${options[@]}"
set_interactive_config_value RELEASE "${DIALOG_RESULT}"
[[ -z ${RELEASE} ]] && exit_with_error "No release selected"
return 0
}
function interactive_config_ask_desktop_build() {
[[ $HAS_VIDEO_OUTPUT == no || $BUILD_MINIMAL == yes ]] && BUILD_DESKTOP=no
[[ -n ${BUILD_DESKTOP} ]] && return 0
set_distribution_status
options=()
options+=("no" "Image with console interface (server)")
options+=("yes" "Image with desktop environment")
dialog_if_terminal_set_vars --title "Choose image type" --backtitle "$backtitle" --no-tags \
--menu "Select the target image type" $TTY_Y $TTY_X $((TTY_Y - 8)) "${options[@]}"
set_interactive_config_value BUILD_DESKTOP "${DIALOG_RESULT}"
unset options
[[ -z $BUILD_DESKTOP ]] && exit_with_error "No image type selected"
if [[ ${BUILD_DESKTOP} == "yes" ]]; then
set_interactive_config_value BUILD_MINIMAL no
SELECTED_CONFIGURATION="desktop"
fi
return 0
}
function interactive_config_ask_standard_or_minimal() {
[[ "${BUILDING_IMAGE}" != "yes" ]] && return 0
[[ $BUILD_DESKTOP != no ]] && return 0
[[ -n $BUILD_MINIMAL ]] && return 0
options=()
options+=("no" "Standard image with console interface")
options+=("yes" "Minimal image with console interface")
dialog_if_terminal_set_vars --title "Choose image type" --backtitle "$backtitle" --no-tags \
--menu "Select the target image type" $TTY_Y $TTY_X $((TTY_Y - 8)) "${options[@]}"
set_interactive_config_value BUILD_MINIMAL "${DIALOG_RESULT}"
unset options
[[ -z $BUILD_MINIMAL ]] && exit_with_error "No standard/minimal selected"
if [[ $BUILD_MINIMAL == "yes" ]]; then
SELECTED_CONFIGURATION="cli_minimal"
else
SELECTED_CONFIGURATION="cli_standard"
fi
return 0
}