[][openwrt-24][common][image][Add unified autobuild framework]
[Description]
Add unified autobuild framework
[Release-log]
N/A
Change-Id: I6d070071082899f024cd34cae7b8209d46452592
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9541683
diff --git a/autobuild/unified/.gitignore b/autobuild/unified/.gitignore
new file mode 100644
index 0000000..862a1cc
--- /dev/null
+++ b/autobuild/unified/.gitignore
@@ -0,0 +1,8 @@
+/*
+!/scripts
+!/global
+!/filogic
+
+!/autobuild.sh
+!/rules
+!.gitignore
diff --git a/autobuild/unified/autobuild.sh b/autobuild/unified/autobuild.sh
new file mode 100755
index 0000000..ddfa76c
--- /dev/null
+++ b/autobuild/unified/autobuild.sh
@@ -0,0 +1,354 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Autobuild framework for OpenWrt
+
+# Autobuild script base directory
+ab_root="$(dirname "$(readlink -f "$0")")"
+
+. ${ab_root}/scripts/log.sh
+. ${ab_root}/scripts/list.sh
+. ${ab_root}/scripts/kconfig.sh
+. ${ab_root}/scripts/openwrt_helpers.sh
+. ${ab_root}/scripts/ab-common.sh
+
+# Command line processing
+
+## Parse autobuild branch name
+ab_branch=
+ab_branch_names=
+ab_branch_level=0
+
+if autobuild_branch_name_check "${1}"; then
+ canonicalize_autobuild_branch_name "${1}" ab_branch_names ab_branch
+ shift
+fi
+
+## Stages
+ab_stages="prepare build release"
+do_menuconfig=
+do_help=
+do_list=
+do_clean=
+
+if list_find ab_stages "${1}"; then
+ ab_stages="${1}"
+ shift
+elif test x"${1}" = x"sdk_release"; then
+ ab_stages="prepare sdk_release"
+ shift
+elif test x"${1}" = x"menuconfig"; then
+ ab_stages="prepare menuconfig"
+ do_menuconfig=1
+ shift
+elif test x"${1}" = x"help"; then
+ ab_stages="help"
+ do_help=1
+ shift
+elif test x"${1}" = x"list"; then
+ do_list=1
+ shift
+elif test x"${1}" = x"clean"; then
+ do_clean=1
+ shift
+fi
+
+## Options
+__logged_args=()
+for arg in "$@"; do
+ if expr index ${arg} '=' 2>&1 >/dev/null; then
+ name=${arg%%=*}
+ value=${arg#*=}
+
+ eval ${name}=\"\${value}\"
+ eval ${name}_set=yes
+
+ __logged_args[${#__logged_args[@]}]="${name}=\"${value}\""
+ else
+ eval ${arg}_set=yes
+
+ __logged_args[${#__logged_args[@]}]="${arg}"
+ fi
+done
+
+IFS=$'\n' __sorted_args=($(sort <<< "${__logged_args[*]}")); unset IFS
+IFS=' ' ab_cmdline="${__logged_args[@]}"; unset IFS
+
+# Enable debugging log?
+if test x"${debug_set}" = x"yes"; then
+ enable_log_debug
+fi
+
+# Enable logging to file?
+if test x"${log_file_set}" = x"yes"; then
+ log_file_merge=
+
+ [ x"${log_file_merge_set}" = x"yes" ] && log_file_merge=1
+
+ set_log_file_prefix "${log_file}" ${log_file_merge}
+fi
+
+# OK, do list here if required
+if test x"${do_list}" = x"1"; then
+ list_all_autobuild_branches
+ exit 0
+fi
+
+openwrt_root="$(pwd)"
+
+if ! is_openwrt_build_root "${openwrt_root}"; then
+ log_err "${openwrt_root} is not a OpenWrt root directory."
+ log_err "The autobuild script must be called from within the OpenWrt root directory."
+ exit 1
+fi
+
+# OpenWrt branch (master, 21.02, ...)
+openwrt_branch=$(openwrt_get_branch)
+
+if test -z "${openwrt_branch}"; then
+ log_err "Failed to get OpenWrt's branch"
+ exit 1
+else
+ log_info "OpenWrt's branch is ${openwrt_branch}"
+fi
+
+# Temporary directory for storing configs and intermediate files
+ab_tmp="${openwrt_root}/.ab"
+
+# Default release directory
+ab_bin_release="${openwrt_root}/autobuild_release"
+
+# OK, do clean here if required
+if test x"${do_clean}" = x"1"; then
+ if ! git status >/dev/null; then
+ log_warn "The clean stage can only be applied if the OpenWrt source is managed by Git."
+ exit 1
+ fi
+
+ exec_log "git -C \"${openwrt_root}\" checkout ."
+ exec_log "git -C \"${openwrt_root}\" clean -f -d -e autobuild"
+ exec_log "rm -rf \"${openwrt_root}/feeds\""
+ exec_log "rm -rf \"${openwrt_root}/package/feeds\""
+ exec_log "rm -rf \"${openwrt_root}/.ab\""
+
+ exit 0
+fi
+
+# Check if we need prepare for build stage
+if list_find ab_stages build; then
+ if test \( ! -f "${ab_tmp}/branch_name" \) -o -f "${ab_tmp}/.stamp.menuconfig"; then
+ list_add_before_unique ab_stages build prepare
+ fi
+fi
+
+# Check for prepare stage
+if list_find ab_stages prepare; then
+ if test -f "${ab_tmp}/branch_name"; then
+ # If prepare stage has been done before, check whether clean is required
+
+ if test -z "${do_menuconfig}" -a -f "${ab_tmp}/.stamp.menuconfig"; then
+ log_warn "This OpenWrt source code has already been prepared for menuconfig."
+ log_warn "Please call \`${0} clean' first."
+ exit 1
+ fi
+
+ if test -n "${do_menuconfig}" -a ! -f "${ab_tmp}/.stamp.menuconfig"; then
+ log_warn "This OpenWrt source code has already been prepared, but not for menuconfig."
+ log_warn "Please call \`${0} clean' first."
+ exit 1
+ fi
+
+ last_ab_branch=$(cat "${ab_tmp}/branch_name")
+
+ if test -n "${ab_branch}"; then
+ if test x"${ab_branch}" != x"${last_ab_branch}"; then
+ log_warn "Autobuild branch name has changed."
+ log_warn "Please call \`${0} clean' first."
+ exit 1
+ fi
+
+ if test -z "${do_menuconfig}"; then
+ last_ab_cmdline=$(cat "${ab_tmp}/cmdline")
+
+ if test x"${ab_cmdline}" != x"${last_ab_cmdline}"; then
+ log_warn "Autobuild configuration has changed."
+ log_warn "Please call \`${0} clean' first."
+ exit 1
+ fi
+ fi
+
+ # Configuration unchanged
+ else
+ # Read previous configuration
+ canonicalize_autobuild_branch_name "${last_ab_branch}" ab_branch_names ab_branch
+ fi
+
+ # prepare stage is not needed
+ list_del ab_stages prepare
+ else
+ if test -z "${ab_branch}" -a x"${do_help}" != x"1"; then
+ log_err "Autobuild branch name is invalid or not specified."
+ print_text "Quick start:"
+ print_text "- To show detailed help:"
+ log_info_raw " ${0} help"
+ print_text "- To start full build for a branch (<branch-name> example: mtxxxx-mtxxxx-xxx):"
+ log_info_raw " ${0} <branch-name>"
+ print_text "- To continue current build:"
+ log_info_raw " ${0} build"
+ print_text "- To clean everything under OpenWrt source tree:"
+ log_info_raw " ${0} clean"
+ print_text "- To start menuconfig and update defconfig for specified branch:"
+ log_info_raw " ${0} <branch-name> menuconfig"
+ print_text "- To list all available branch names:"
+ log_info_raw " ${0} <branch-name> list"
+ exit 1
+ fi
+ fi
+else
+ if test -z "${ab_branch}" -a -f "${ab_tmp}/branch_name"; then
+ last_ab_branch=$(cat "${ab_tmp}/branch_name")
+
+ # Read previous configuration
+ canonicalize_autobuild_branch_name "${last_ab_branch}" ab_branch_names ab_branch
+ fi
+fi
+
+## Fill branch names
+ab_branch_level=${#ab_branch_names[@]}
+ab_branch_platform=${ab_branch_names[0]}
+ab_branch_wifi=${ab_branch_names[1]}
+ab_branch_sku=${ab_branch_names[2]}
+ab_branch_variant=${ab_branch_names[3]}
+
+## Set and print branch configuration
+[ -n "${ab_branch}" ] && log_info "Autobuild branch: ${ab_branch}"
+
+if test -n "${ab_branch_platform}"; then
+ ab_branch_level=1
+ ab_platform_dir=${ab_root}/${ab_branch_platform}
+ print_conf "Platform" "${ab_branch_platform}"
+fi
+
+if test -n "${ab_branch_wifi}"; then
+ ab_branch_level=2
+ ab_wifi_dir=${ab_platform_dir}/${ab_branch_wifi}
+ print_conf "WiFi" "${ab_branch_wifi}"
+fi
+
+if test -n "${ab_branch_sku}"; then
+ ab_branch_level=3
+ ab_sku_dir=${ab_wifi_dir}/${ab_branch_sku}
+ print_conf "SKU" "${ab_branch_sku}"
+fi
+
+if test -n "${ab_branch_variant}"; then
+ ab_branch_level=4
+ ab_variant_dir=${ab_sku_dir}/${ab_branch_variant}
+ print_conf "Variant" "${ab_branch_variant}"
+fi
+
+# Setup global settings
+
+## Set new binary release directory
+if test x"${release_dir_set}" = x"yes"; then
+ ab_bin_release="${openwrt_root}/${release_dir}"
+fi
+
+ab_bin_release="${ab_bin_release}/${ab_branch}"
+
+## Set global directories
+ab_global=${ab_root}/global
+
+# Setup help text
+help_add_line ""
+help_add_line "Autobuild script for OpenWrt"
+help_add_line ""
+help_add_line "Usage: autobuild.sh [branch] [stage] [options...]"
+help_add_line ""
+help_add_line "Branch: <platform>[-<wifi>[-<sku>[-<variant>]]]"
+help_add_line " Branch is only required for prepare. It can be omitted for build/release"
+help_add_line " after a successful prepare."
+help_add_line ""
+help_add_line "Stages:"
+help_add_line " (If stage is not specified, default will be \"prepare/build/release\")"
+help_add_line " prepare - Prepare the OpenWrt source code."
+help_add_line " Do patching, copying/deleting files."
+help_add_line " Once prepared, clean must be done before another prepare."
+help_add_line " build - Do actual build."
+help_add_line " release - Collect built binary files only."
+help_add_line " sdk_release - Do source code release. MediaTek internal use only."
+help_add_line " menuconfig - Do menuconfig for specified branch. defconfig of this branch"
+help_add_line " will be updated."
+help_add_line " clean - Clean all modified/untraced files/directories from OpenWrt"
+help_add_line " source code."
+help_add_line " list - List all available branch names."
+help_add_line " help - Show this help."
+help_add_line ""
+help_add_line "Options:"
+help_add_line " The options can be <key>=<value>, or just simple <key>."
+help_add_line " log_file=<file> - Enable log output to file."
+help_add_line " log_file_merge - Log stdout and stderr to one file."
+help_add_line " debug - Enable debug log output."
+help_add_line " release_dir=<dir> - Override default release directory."
+help_add_line " Default directory is 'autobuild_release' under OpenWrt's source directory."
+
+# Include branch rules (the rule is child level overriding parent level)
+. "${ab_root}/rules"
+[ -f "${ab_global}/${openwrt_branch}/rules" ] && . "${ab_global}/${openwrt_branch}/rules"
+[ -n "${ab_platform_dir}" ] && . "${ab_platform_dir}/rules"
+[ -n "${ab_wifi_dir}" ] && . "${ab_wifi_dir}/rules"
+[ -n "${ab_sku_dir}" ] && . "${ab_sku_dir}/rules"
+[ -n "${ab_variant_dir}" ] && . "${ab_variant_dir}/rules"
+
+# Show help?
+if test x"${do_help}" = x"1"; then
+ help_print
+ exit 0
+fi
+
+# Run stages
+log_dbg "All stages: ${ab_stages}"
+
+for stage in ${ab_stages}; do
+ substages=$(get_substages "${stage}")
+
+ prompt_stage "Current stage: \"${stage}\""
+ log_dbg "Substages of ${stage}: ${substages}"
+
+ for substage in ${substages}; do
+ hooks=$(get_hooks "${substage}")
+
+ prompt_stage "Current substage: \"${substage}\""
+
+ [ -z "${hooks}" ] && hooks="${substage}"
+ clean_hooks hooks
+
+ if test -z "${hooks}"; then
+ log_info "Nothing to do with substage ${substage}"
+ continue
+ fi
+
+ log_dbg "Hooks of substage \"${substage}\": ${hooks}"
+
+ for hook in ${hooks}; do
+ prompt_stage "Executing hook \"${hook}\""
+
+ eval "${hook}"
+
+ ret=$?
+
+ if test ${ret} != 0; then
+ log_err "${stage}/${substage}/${hook} exited with error code ${ret}"
+ exit 1
+ fi
+ done
+ done
+done
+
+# All done
+if list_find ab_stages build; then
+ log_info "Autobuild finished"
+fi
+
+exit 0
diff --git a/autobuild/unified/global/kasan.sh b/autobuild/unified/global/kasan.sh
new file mode 100755
index 0000000..f90baf7
--- /dev/null
+++ b/autobuild/unified/global/kasan.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Rules for enabling KASAN
+
+enable_kasan_openwrt() {
+ openwrt_config_enable CONFIG_KERNEL_KASAN
+ openwrt_config_enable CONFIG_KERNEL_KASAN_GENERIC
+ openwrt_config_enable CONFIG_KERNEL_KALLSYMS
+ openwrt_config_enable CONFIG_KERNEL_KASAN_OUTLINE
+ openwrt_config_disable CONFIG_KERNEL_KASAN_INLINE
+ openwrt_config_enable CONFIG_KERNEL_SLUB_DEBUG
+ openwrt_config_enable CONFIG_KERNEL_FRAME_WARN 4096
+}
+
+enable_kasan_kernel() {
+ kernel_config_enable CONFIG_DEBUG_KMEMLEAK
+ kernel_config_enable CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN
+ kernel_config_disable CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF
+ kernel_config_enable CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE 16000
+ kernel_config_enable CONFIG_DEBUG_KMEMLEAK_TEST m
+ # Still need to enable kernel configs for 21.02
+ kernel_config_enable CONFIG_KALLSYMS
+ kernel_config_enable CONFIG_KASAN
+ kernel_config_enable CONFIG_KASAN_GENERIC
+ kernel_config_disable CONFIG_KASAN_INLINE
+ kernel_config_enable CONFIG_KASAN_OUTLINE
+ kernel_config_enable CONFIG_KASAN_SHADOW_OFFSET 0xdfffffd000000000
+ kernel_config_disable CONFIG_TEST_KASAN
+ kernel_config_enable CONFIG_SLUB_DEBUG
+ kernel_config_enable CONFIG_FRAME_WARN 4096
+}
+
+if test x"${kasan_set}" == x"yes"; then
+ list_add_before $(hooks autobuild_prepare) make_defconfig enable_kasan_openwrt
+ list_add_after $(hooks autobuild_prepare) variant_change_kernel_config enable_kasan_kernel
+fi
+
+help_add_line " kasan - Enable KASAN."
diff --git a/autobuild/unified/global/master/patches-base/0001-kernel-build-mk-do-not-strip-kernel-debug-modules.patch b/autobuild/unified/global/master/patches-base/0001-kernel-build-mk-do-not-strip-kernel-debug-modules.patch
new file mode 100644
index 0000000..62e47b4
--- /dev/null
+++ b/autobuild/unified/global/master/patches-base/0001-kernel-build-mk-do-not-strip-kernel-debug-modules.patch
@@ -0,0 +1,17 @@
+From: Weijie Gao <weijie.gao@mediatek.com>
+Subject: [PATCH] kernel-build.mk: do not strip kernel debug modules
+
+Only the full ELF file is usable for TRACE32 analysis
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+
+--- a/include/kernel-build.mk
++++ b/include/kernel-build.mk
+@@ -65,7 +65,6 @@ ifdef CONFIG_COLLECT_KERNEL_DEBUG
+ -$(CP) \
+ $(STAGING_DIR_ROOT)/lib/modules/$(LINUX_VERSION)/*.ko \
+ $(KERNEL_BUILD_DIR)/debug/modules/
+- $(FIND) $(KERNEL_BUILD_DIR)/debug -type f | $(XARGS) $(KERNEL_CROSS)strip --only-keep-debug
+ $(TAR) c -C $(KERNEL_BUILD_DIR) debug \
+ $(if $(SOURCE_DATE_EPOCH),--mtime="@$(SOURCE_DATE_EPOCH)") \
+ | zstd -T0 -f -o $(BIN_DIR)/kernel-debug.tar.zst
diff --git a/autobuild/unified/rules b/autobuild/unified/rules
new file mode 100755
index 0000000..449fb3d
--- /dev/null
+++ b/autobuild/unified/rules
@@ -0,0 +1,588 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Top rules
+
+# Declare substages of every stage
+sub_stage_prepare="mk_ab_tmp pre_prepare apply_feed_script_patch modify_feeds_conf update_feeds \
+ mtk_feed_prepare install_feeds autobuild_prepare post_prepare"
+sub_stage_build="pre_build do_build post_build"
+sub_stage_release="pre_release do_release post_release"
+sub_stage_sdk_release="pre_sdk_release do_sdk_release post_sdk_release"
+sub_stage_menuconfig="do_menuconfig_update"
+
+# Declare hooks of every substage
+hooks_pre_prepare=
+hooks_mtk_feed_prepare="remove_files_by_mtk_feed_list copy_mtk_feed_files apply_mtk_feed_base_patches \
+ apply_mtk_feed_feed_patches"
+hooks_autobuild_prepare="remove_files_by_global_list copy_global_files apply_global_patches \
+ remove_files_by_platform_list copy_platform_files apply_platform_patches \
+ remove_files_by_wifi_list copy_wifi_files apply_wifi_patches \
+ remove_files_by_sku_list copy_sku_files apply_sku_patches \
+ remove_files_by_variant_list copy_variant_files apply_variant_patches \
+ prepare_wifi_driver prepare_openwrt_config platform_change_openwrt_config \
+ wifi_change_openwrt_config sku_change_openwrt_config variant_change_openwrt_config \
+ enable_openwrt_collect_debug_symbols make_defconfig update_target_info \
+ platform_change_kernel_config wifi_change_kernel_config sku_change_kernel_config \
+ variant_change_kernel_config enable_kernel_proc_config_gz"
+hooks_post_prepare="prepare_stamp"
+
+hooks_pre_build=
+hooks_do_build="download_openwrt_packages build_openwrt"
+hooks_post_build=
+
+hooks_pre_release="update_target_info mk_ab_bin_release"
+hooks_do_release="collect_openwrt_images collect_openwrt_configs collect_kernel_debug_symbols \
+ collect_userspace_debug_symbols collect_feeds_buildinfo"
+hooks_post_release=
+
+hooks_pre_sdk_release=
+hooks_do_sdk_release="release_openwrt_sdk"
+hooks_post_sdk_release=
+
+# Global information
+build_time=
+internal_build=
+mtk_feed_path=
+kernel_ver=
+target_name=
+subtarget_name=
+
+openwrt_bin_dir=
+openwrt_config_file=
+kernel_config_file=
+
+# Generic function for modifying openwrt config, to be compliant with menuconfig
+# $1: Config name
+openwrt_config_disable() {
+ if test -z "${do_menuconfig}"; then
+ if kconfig_enabled "${openwrt_config_file}" "${1}"; then
+ kconfig_disable "${openwrt_config_file}" "${1}"
+ fi
+ fi
+}
+
+# $1: Config name
+# $2: Value to be set for config (y if not specified)
+openwrt_config_enable() {
+ if test -z "${do_menuconfig}"; then
+ if ! kconfig_enabled "${openwrt_config_file}" "${1}"; then
+ kconfig_enable "${openwrt_config_file}" "${1}" "${2}"
+ fi
+ fi
+}
+
+# $1: Config name
+openwrt_config_enabled() {
+ kconfig_enabled "${openwrt_config_file}" "${1}"
+}
+
+# Generic function for modifying target's kernel config
+# $1: Config name
+kernel_config_disable() {
+ if kconfig_enabled "${kernel_config_file}" "${1}"; then
+ kconfig_disable "${kernel_config_file}" "${1}"
+ fi
+}
+
+# $1: Config name
+# $2: Value to be set for config (y if not specified)
+kernel_config_enable() {
+ if ! kconfig_enabled "${kernel_config_file}" "${1}"; then
+ kconfig_enable "${kernel_config_file}" "${1}" "${2}"
+ fi
+}
+
+# $1: Config name
+kernel_config_enabled() {
+ kconfig_enabled "${kernel_config_file}" "${1}"
+}
+
+# Create the ${ab_tmp} directory
+mk_ab_tmp() {
+ exec_log "mkdir -p \"${ab_tmp}\""
+}
+
+# Modify scripts/feed to allow specify subdirectory for scanning
+apply_feed_script_patch() {
+ apply_patch "${ab_root}/scripts/${openwrt_branch}/scripts-feeds-support-subdir.patch"
+}
+
+modify_feeds_conf() {
+ local rev_file_list=
+ local feed_rev=
+ local feed_url="https://git01.mediatek.com/openwrt/feeds/mtk-openwrt-feeds"
+
+ # Backup original feeds
+ exec_log "cp -f \"${openwrt_root}/feeds.conf.default\" \"${ab_tmp}/\""
+
+ # Modify feeds
+ openwrt_feeds_replace_url packages https://gerrit.mediatek.inc/openwrt/feeds/packages
+ openwrt_feeds_replace_url luci https://gerrit.mediatek.inc/openwrt/feeds/luci
+ openwrt_feeds_replace_url routing https://gerrit.mediatek.inc/openwrt/feeds/routing
+ openwrt_feeds_disable telephony
+ openwrt_feeds_change_src_git_type packages 0
+ openwrt_feeds_change_src_git_type luci 0
+ openwrt_feeds_change_src_git_type routing 0
+
+ # Add mtk-openwrt-feeds
+ [ -n "${ab_variant_dir}" ] && list_append rev_file_list "${ab_variant_dir}/feed_revision"
+ [ -n "${ab_sku_dir}" ] && list_append rev_file_list "${ab_sku_dir}/feed_revision"
+ [ -n "${ab_wifi_dir}" ] && list_append rev_file_list "${ab_wifi_dir}/feed_revision"
+ [ -n "${ab_platform_dir}" ] && list_append rev_file_list "${ab_platform_dir}/feed_revision"
+ list_append rev_file_list "${ab_root}/feed_revision"
+
+ for rev_file in ${rev_file_list}; do
+ if test -f "${rev_file}"; then
+ feed_rev=$(cat "${rev_file}")
+ break;
+ fi
+ done
+
+ [ -n "${feed_rev}" ] && feed_rev="^${feed_rev}"
+
+ if test -d "${openwrt_root}/../mtk-openwrt-feeds"; then
+ feed_url="${openwrt_root}/../mtk-openwrt-feeds"
+ internal_build=1
+
+ log_dbg "Internal repo build mode"
+ fi
+
+ if test -n "${internal_build}" -a -z "${feed_rev}"; then
+ openwrt_feeds_add mtk_openwrt_feed src-link "${feed_url}" --subdir=feed
+ else
+ openwrt_feeds_add mtk_openwrt_feed src-git "${feed_url}${feed_rev}" --subdir=feed
+ fi
+}
+
+update_feeds() {
+ exec_log "${openwrt_root}/scripts/feeds update -a"
+
+ mtk_feed_path=${openwrt_root}/feeds/mtk_openwrt_feed
+}
+
+remove_files_by_mtk_feed_list() {
+ remove_files_from_list "${mtk_feed_path}/common/remove_list.txt"
+ remove_files_from_list "${mtk_feed_path}/${openwrt_branch}/remove_list.txt"
+}
+
+copy_mtk_feed_files() {
+ copy_files "${mtk_feed_path}/common/files"
+ copy_files "${mtk_feed_path}/tools" tools
+ copy_files "${mtk_feed_path}/${openwrt_branch}/files"
+}
+
+apply_mtk_feed_base_patches() {
+ apply_patches "${mtk_feed_path}/${openwrt_branch}/patches-base"
+}
+
+apply_mtk_feed_feed_patches() {
+ apply_patches "${mtk_feed_path}/${openwrt_branch}/patches-feeds"
+}
+
+install_feeds() {
+ exec_log "${openwrt_root}/scripts/feeds install -a"
+}
+
+remove_files_by_global_list() {
+ remove_files_from_list "${ab_global}/common/remove_list.txt"
+ remove_files_from_list "${ab_global}/${openwrt_branch}/remove_list.txt"
+}
+
+copy_global_files() {
+ copy_files "${ab_global}/common/files"
+ copy_files "${ab_global}/${openwrt_branch}/files"
+}
+
+apply_global_patches() {
+ apply_patches "${ab_global}/${openwrt_branch}/patches-base" || return 1
+ apply_patches "${ab_global}/${openwrt_branch}/patches-feeds" || return 1
+ apply_patches "${ab_global}/${openwrt_branch}/patches" || return 1
+}
+
+remove_files_by_platform_list() {
+ remove_files_from_list "${ab_platform_dir}/remove_list.txt"
+ remove_files_from_list "${ab_platform_dir}/${openwrt_branch}/remove_list.txt"
+}
+
+copy_platform_files() {
+ copy_files "${ab_platform_dir}/files"
+ copy_files "${ab_platform_dir}/${openwrt_branch}/files"
+}
+
+apply_platform_patches() {
+ apply_patches "${ab_platform_dir}/${openwrt_branch}/patches-base" || return 1
+ apply_patches "${ab_platform_dir}/${openwrt_branch}/patches-feeds" || return 1
+ apply_patches "${ab_platform_dir}/${openwrt_branch}/patches" || return 1
+}
+
+remove_files_by_wifi_list() {
+ if test -n "${ab_wifi_dir}"; then
+ remove_files_from_list "${ab_wifi_dir}/remove_list.txt"
+ remove_files_from_list "${ab_wifi_dir}/${openwrt_branch}/remove_list.txt"
+ fi
+}
+
+copy_wifi_files() {
+ if test -n "${ab_wifi_dir}"; then
+ copy_files "${ab_wifi_dir}/files"
+ copy_files "${ab_wifi_dir}/${openwrt_branch}/files"
+ fi
+}
+
+apply_wifi_patches() {
+ if test -n "${ab_wifi_dir}"; then
+ apply_patches "${ab_wifi_dir}/${openwrt_branch}/patches-base" || return 1
+ apply_patches "${ab_wifi_dir}/${openwrt_branch}/patches-feeds" || return 1
+ apply_patches "${ab_wifi_dir}/${openwrt_branch}/patches" || return 1
+ fi
+}
+
+remove_files_by_sku_list() {
+ if test -n "${ab_sku_dir}"; then
+ remove_files_from_list "${ab_sku_dir}/remove_list.txt"
+ remove_files_from_list "${ab_sku_dir}/${openwrt_branch}/remove_list.txt"
+ fi
+}
+
+copy_sku_files() {
+ if test -n "${ab_sku_dir}"; then
+ copy_files "${ab_sku_dir}/files"
+ copy_files "${ab_sku_dir}/${openwrt_branch}/files"
+ fi
+}
+
+apply_sku_patches() {
+ if test -n "${ab_sku_dir}"; then
+ apply_patches "${ab_sku_dir}/${openwrt_branch}/patches-base" || return 1
+ apply_patches "${ab_sku_dir}/${openwrt_branch}/patches-feeds" || return 1
+ apply_patches "${ab_sku_dir}/${openwrt_branch}/patches" || return 1
+ fi
+}
+
+remove_files_by_variant_list() {
+ if test -n "${ab_variant_dir}"; then
+ remove_files_from_list "${ab_sku_dir}/remove_list.txt"
+ remove_files_from_list "${ab_sku_dir}/${openwrt_branch}/remove_list.txt"
+ fi
+}
+
+copy_variant_files() {
+ if test -n "${ab_variant_dir}"; then
+ copy_files "${ab_variant_dir}/files"
+ copy_files "${ab_variant_dir}/${openwrt_branch}/files"
+ fi
+}
+
+apply_variant_patches() {
+ if test -n "${ab_variant_dir}"; then
+ apply_patches "${ab_variant_dir}/${openwrt_branch}/patches-base" || return 1
+ apply_patches "${ab_variant_dir}/${openwrt_branch}/patches-feeds" || return 1
+ apply_patches "${ab_variant_dir}/${openwrt_branch}/patches" || return 1
+ fi
+}
+
+# prepare_wifi_driver() { } Implemented by wifi inclusion
+
+prepare_openwrt_config() {
+ local kconfig_expr=
+ local kconfig_files="\"${ab_platform_dir}/${openwrt_branch}/defconfig\""
+
+ openwrt_config_file="${openwrt_root}/.config"
+
+ if test ${ab_branch_level} -ge 2; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_wifi_dir}/${openwrt_branch}/defconfig\""
+
+ if test -f "${ab_wifi_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_wifi_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test ${ab_branch_level} -ge 3; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_sku_dir}/${openwrt_branch}/defconfig\""
+
+ if test -f "${ab_sku_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_sku_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test ${ab_branch_level} -ge 4; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_variant_dir}/${openwrt_branch}/defconfig\""
+
+ if test -f "${ab_variant_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_variant_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test -n "${kconfig_expr}" -a -n "${kconfig_files}"; then
+ exec_log "\"${openwrt_root}/scripts/kconfig.pl\" ${kconfig_expr} ${kconfig_files} > \"${ab_tmp}/.config\"" 1
+ else
+ exec_log "cp ${kconfig_files} \"${ab_tmp}/.config\""
+ fi
+
+ exec_log "rm -f \"${openwrt_root}/.config.old\""
+ exec_log "make -C \"${openwrt_root}\" -f \"${ab_root}/scripts/openwrt_kconfig.mk\" loaddefconfig CONFIG_FILE=\"${ab_tmp}/.config\""
+ exec_log "cp -f \"${openwrt_root}/.config\" \"${ab_tmp}/.config.prev\""
+}
+
+# {platform,wifi,sku,variant}_change_openwrt_config() { } Implemented by platform sub levels
+
+enable_openwrt_collect_debug_symbols() {
+ openwrt_config_enable CONFIG_COLLECT_KERNEL_DEBUG
+ openwrt_config_enable CONFIG_DEBUG
+ openwrt_config_disable CONFIG_KERNEL_DEBUG_INFO_REDUCED
+}
+
+make_defconfig() {
+ if test -f "${ab_tmp}/.config.prev" -a -f "${openwrt_root}/.config"; then
+ if ! cmp -s "${ab_tmp}/.config.prev" "${openwrt_root}/.config"; then
+ exec_log "make -C \"${openwrt_root}\" defconfig"
+ fi
+ fi
+}
+
+__target_info_updated=
+
+update_target_info() {
+ [ -n "${__target_info_updated}" ] && return 0
+
+ target_name=$(openwrt_get_target_name)
+ subtarget_name=$(openwrt_get_subtarget_name)
+
+ if test -z "${target_name}"; then
+ log_err "Failed to get OpenWrt's target name"
+ return 1
+ fi
+
+ log_info "Target name: ${target_name}"
+ [ -n "${subtarget_name}" ] && log_info "Subtarget name: ${subtarget_name}"
+
+ openwrt_bin_dir=$(openwrt_get_bin_dir)
+
+ if test -z "${openwrt_bin_dir}"; then
+ log_err "Failed to get OpenWrt's bin directory"
+ return 1
+ fi
+
+ log_dbg "Target bin dir: ${openwrt_bin_dir}"
+
+ kernel_ver=$(openwrt_get_target_kernel_version ${target_name})
+
+ if test -z "${kernel_ver}"; then
+ log_err "Failed to get OpenWrt's target kernel version"
+ return 1
+ else
+ log_info "Target kernel version: ${kernel_ver}"
+ fi
+
+ if test -n ${subtarget_name}; then
+ if test -f "${openwrt_root}/target/linux/${target_name}/${subtarget_name}/config-${kernel_ver}"; then
+ kernel_config_file="${openwrt_root}/target/linux/${target_name}/${subtarget_name}/config-${kernel_ver}"
+ fi
+ fi
+
+ if test -z ${kernel_config_file}; then
+ if test -f "${openwrt_root}/target/linux/${target_name}/config-${kernel_ver}"; then
+ kernel_config_file="${openwrt_root}/target/linux/${target_name}/config-${kernel_ver}"
+ fi
+ fi
+
+ if test -z ${kernel_config_file}; then
+ log_err "Unable to find target's kernel config file"
+ return 1
+ fi
+
+ log_dbg "Target kernel config file: ${kernel_config_file}"
+
+ __target_info_updated=1
+}
+
+# {platform,wifi,sku,variant}_change_kernel_config() { } Implemented by platform sub levels
+
+enable_kernel_proc_config_gz() {
+ kernel_config_enable CONFIG_IKCONFIG
+ kernel_config_enable CONFIG_IKCONFIG_PROC
+}
+
+prepare_stamp() {
+ if test -n "${do_menuconfig}"; then
+ touch "${ab_tmp}/.stamp.menuconfig"
+ else
+ rm -f "${ab_tmp}/.stamp.menuconfig"
+ fi
+
+ echo -n "${ab_branch}" > "${ab_tmp}/branch_name"
+ echo -n "${ab_cmdline}" > "${ab_tmp}/cmdline"
+}
+
+download_openwrt_packages() {
+ if test x"${internal_build}" = x"1"; then
+ if ! [ -d "${openwrt_root}/dl" -o -L "${openwrt_root}/dl" ]; then
+ exec_log "ln -sf ../dl \"${openwrt_root}/dl\""
+ fi
+ fi
+
+ exec_log "make -C \"${openwrt_root}\" V=1 -j\$((\$(nproc) + 1)) download"
+}
+
+build_openwrt() {
+ local ret=
+ local verbose=1
+
+ if test x"${debug_set}" = x"yes"; then
+ verbose=s
+ fi
+
+ build_time=$(date +%Y%m%d%H%M%S)
+
+ exec_log "make -C \"${openwrt_root}\" V=${verbose} -j\$((\$(nproc) + 1))"
+
+ ret=$?
+
+ if test ${ret} != 0; then
+ log_warn "Build failed with error code ${ret}."
+ log_warn "Restart single-threaded building for debugging purpose."
+
+ exec_log "make -C \"${openwrt_root}\" V=s -j1"
+
+ ret=$?
+
+ if test ${ret} != 0; then
+ log_err "Debug build failed with error code ${ret}."
+ return 1
+ fi
+ fi
+
+ log_info "OpenWrt built successfully"
+}
+
+# Create the ${ab_bin_release} directory
+mk_ab_bin_release() {
+ exec_log "mkdir -p \"${ab_bin_release}\""
+}
+
+collect_openwrt_images() {
+ local file_count=0
+ local files=
+
+ if [ -z "${build_time}" ]; then
+ build_time=$(date +%Y%m%d%H%M%S)
+ fi
+
+ files=$(find "${openwrt_bin_dir}" -maxdepth 1 -name '*.bin' -o -name "*.img" -o -name '*.itb' -o -name '*.gz')
+
+ for file in ${files}; do
+ local file_no_ext=${file%.*}
+ local file_name=${file_no_ext##*/}
+ local file_ext=${file##*.}
+
+ exec_log "cp -rf \"${file}\" \"${ab_bin_release}/${file_name}-${build_time}.${file_ext}\""
+ ((file_count++))
+ done
+
+ log_info "Total ${file_count} image files copied."
+}
+
+collect_openwrt_configs() {
+ local linux_dir=$(openwrt_get_target_kernel_linux_build_dir ${target_name})
+
+ exec_log "cp -f \"${openwrt_root}/.config\" \"${ab_bin_release}/openwrt.config\""
+
+ if test -z "${linux_dir}"; then
+ log_warn "Failed to get OpenWrt's linux kernel build directory"
+ else
+ local kernel_config_data="${linux_dir}/kernel/config_data"
+
+ if [ -f "${kernel_config_data}" ]; then
+ exec_log "cp -f \"${kernel_config_data}\" \"${ab_bin_release}/kernel.config\""
+ fi
+ fi
+}
+
+collect_kernel_debug_symbols() {
+ if [ -f "${openwrt_bin_dir}/kernel-debug.tar.zst" ]; then
+ exec_log "cp -f \"${openwrt_bin_dir}/kernel-debug.tar.zst\" \"${ab_bin_release}/\""
+ fi
+}
+
+collect_userspace_debug_symbols() {
+ local staging_dir_root=$(openwrt_get_staging_dir_root)
+
+ log_dbg "Staging dir root: ${staging_dir_root}"
+
+ if test -d "${staging_dir_root}"; then
+ staging_dir_root_prefix=$(dirname "${staging_dir_root}")
+ staging_dir_root_name=$(basename "${staging_dir_root}")
+
+ exec_log "tar -jcf \"${ab_bin_release}/rootfs-debug.tar.bz2\" -C \"${staging_dir_root_prefix}\" \"${staging_dir_root_name}\""
+ fi
+}
+
+collect_feeds_buildinfo() {
+ if [ -f "${openwrt_bin_dir}/feeds.buildinfo" ]; then
+ exec_log "cp -f \"${openwrt_bin_dir}/feeds.buildinfo\" \"${ab_bin_release}/\""
+ fi
+}
+
+do_menuconfig_update() {
+ local kconfig_expr=
+ local kconfig_files=
+ local final_file=
+
+ exec_log "make -C \"${openwrt_root}\" menuconfig"
+
+ if test ${ab_branch_level} -eq 1; then
+ exec_log "make -C ${openwrt_root} -f \"${ab_root}/scripts/openwrt_kconfig.mk\" savedefconfig CONFIG_FILE=\"${ab_tmp}/defconfig\""
+ exec_log "\"${openwrt_root}/scripts/kconfig.pl\" + \"${ab_tmp}/defconfig\" /dev/null > \"${ab_platform_dir}/${openwrt_branch}/defconfig\"" 1
+ return
+ fi
+
+ exec_log "make -C ${openwrt_root} -f \"${ab_root}/scripts/openwrt_kconfig.mk\" savedefconfig CONFIG_FILE=\"${ab_tmp}/defconfig\""
+
+ if test ${ab_branch_level} -ge 2; then
+ kconfig_files="\"${ab_platform_dir}/${openwrt_branch}/defconfig\""
+ final_file="${ab_wifi_dir}/${openwrt_branch}/defconfig"
+
+ if test -f "${ab_wifi_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_wifi_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test ${ab_branch_level} -ge 3; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${final_file}\""
+ final_file="${ab_sku_dir}/${openwrt_branch}/defconfig"
+
+ if test -f "${ab_sku_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_sku_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test ${ab_branch_level} -ge 4; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${final_file}\""
+ final_file="${ab_variant_dir}/${openwrt_branch}/defconfig"
+
+ if test -f "${ab_variant_dir}/${openwrt_branch}/defconfig_forced"; then
+ kconfig_expr="${kconfig_expr} +"
+ kconfig_files="${kconfig_files} \"${ab_variant_dir}/${openwrt_branch}/defconfig_forced\""
+ fi
+ fi
+
+ if test -n "${kconfig_expr}" -a -n "${kconfig_files}"; then
+ exec_log "\"${openwrt_root}/scripts/kconfig.pl\" ${kconfig_expr} ${kconfig_files} > \"${ab_tmp}/.config.base\"" 1
+ else
+ exec_log "cp ${kconfig_files} \"${ab_tmp}/.config.base\""
+ fi
+
+ exec_log "\"${openwrt_root}/scripts/kconfig.pl\" - \"${ab_tmp}/defconfig\" \"${ab_tmp}/.config.base\" > \"${final_file}\"" 1
+}
diff --git a/autobuild/unified/scripts/21.02/scripts-feeds-support-subdir.patch b/autobuild/unified/scripts/21.02/scripts-feeds-support-subdir.patch
new file mode 100644
index 0000000..af727c2
--- /dev/null
+++ b/autobuild/unified/scripts/21.02/scripts-feeds-support-subdir.patch
@@ -0,0 +1,72 @@
+--- a/scripts/feeds
++++ b/scripts/feeds
+@@ -120,16 +120,22 @@ sub update_location($$)
+ return 0;
+ }
+
+-sub update_index($)
++sub update_index($$)
+ {
+ my $name = shift;
++ my $subdir = shift;
++ my $real_subdir = "";
+
+ -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
+ -d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1;
+
++ if (length( $subdir || '' )) {
++ $real_subdir = "/$subdir";
++ }
++
+ system("$mk -s prepare-mk OPENWRT_BUILD= TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+- system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+- system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
++ system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name$real_subdir\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
++ system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name$real_subdir\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+ system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index");
+ system("ln -sf $name.tmp/.targetinfo ./feeds/$name.targetindex");
+
+@@ -756,11 +762,12 @@ sub uninstall {
+ return 0;
+ }
+
+-sub update_feed($$$$$)
++sub update_feed($$$$$$)
+ {
+ my $type=shift;
+ my $name=shift;
+ my $src=shift;
++ my $subdir=shift;
+ my $perform_update=shift;
+ my $force_update=shift;
+ my $force_relocate=update_location( $name, "@$src" );
+@@ -794,7 +801,7 @@ sub update_feed($$$$$)
+ };
+ };
+ warn "Create index file './feeds/$name.index' \n";
+- update_index($name) == 0 or do {
++ update_index($name, $subdir) == 0 or do {
+ warn "failed.\n";
+ return 1;
+ };
+@@ -830,16 +837,18 @@ sub update {
+ if ( ($#ARGV == -1) or $opts{a}) {
+ foreach my $feed (@feeds) {
+ my ($type, $name, $src) = @$feed;
+- update_feed($type, $name, $src, $perform_update, $opts{f}) == 0 or $failed=1;
++ my $subdir = $feed->[3]{subdir};
++ update_feed($type, $name, $src, $subdir, $perform_update, $opts{f}) == 0 or $failed=1;
+ }
+ } else {
+ while ($feed_name = shift @ARGV) {
+ foreach my $feed (@feeds) {
+ my ($type, $name, $src) = @$feed;
++ my $subdir = $feed->[3]{subdir};
+ if($feed_name ne $name) {
+ next;
+ }
+- update_feed($type, $name, $src, $perform_update, $opts{f}) == 0 or $failed=1;
++ update_feed($type, $name, $src, $subdir, $perform_update, $opts{f}) == 0 or $failed=1;
+ }
+ }
+ }
diff --git a/autobuild/unified/scripts/ab-common.sh b/autobuild/unified/scripts/ab-common.sh
new file mode 100755
index 0000000..b17910e
--- /dev/null
+++ b/autobuild/unified/scripts/ab-common.sh
@@ -0,0 +1,247 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Helpers for Autobuild (Common part)
+
+__help_text=()
+__use_quilt=
+
+if which quilt > /dev/null; then
+ __quilt_ver=$(quilt --version 2>/dev/null)
+ [ -n "${__quilt_ver}" ] && __use_quilt=1
+fi
+
+# Add help text line
+# $1: Text line
+help_add_line() {
+ __help_text[${#__help_text[@]}]="${1}"
+}
+
+# Print help text
+help_print() {
+ for i in "${!__help_text[@]}"; do
+ printf "%s\n" "${__help_text[$i]}"
+ done
+}
+
+# Check whether a string is a valid autobuild branch name
+# $1: Branch name
+autobuild_branch_name_check() {
+ [ -z "${1}" ] && return 1
+
+ local names=$(echo "${1}" | sed 's/-/ /g')
+ local path=
+
+ for field in ${names}; do
+ path="${path}${field}/"
+ if ! test -f "${ab_root}/${path}rules"; then
+ return 1
+ fi
+ done
+
+ return 0
+}
+
+# Canonicalize an autobuild branch name
+# $1: Branch name
+# $2: Name of a array to store the each branch level name
+# $3: Canonicalized branch name
+canonicalize_autobuild_branch_name() {
+ local names=$(echo "${1}" | sed 's/-/ /g')
+ local canonical_name=
+ local name_arr=
+ local i=
+
+ eval "name_arr=(${names})"
+
+ local n=${#name_arr[@]}
+
+ [ ${n} -gt 4 ] && n=4
+
+ [ ${n} -eq 0 ] && return 1
+
+ for i in $(seq 0 $((n-1))); do
+ eval "${2}[$i]=\"${name_arr[$i]}\""
+ canonical_name="${canonical_name}${name_arr[$i]}-"
+ done
+
+ eval "${3}=${canonical_name%-*}"
+
+ return 0
+}
+
+# Clean undefined hooks
+# $1: Hook name
+clean_hooks() {
+ local tmp_hooks=
+ local new_hooks=
+ local hook=
+
+ eval "tmp_hooks=\"\$${1}\""
+
+ for hook in ${tmp_hooks}; do
+ if type ${hook} >/dev/null 2>&1; then
+ new_hooks="${new_hooks} ${hook}"
+ fi
+ done
+
+ eval "${1}=\"\${new_hooks}\""
+}
+
+# Return the substage list name of a stage
+# $1: Stage name
+substage() {
+ echo "sub_stage_${1}"
+}
+
+# Return the hook list name of a substage
+# $1: Substage name
+hooks() {
+ echo "hooks_${1}"
+}
+
+# Return the substage list of a stage
+# $1: Stage name
+get_substages() {
+ eval "echo \"\${sub_stage_${1}}\""
+}
+
+# Return the hook list of a substage
+# $1: Substage name
+get_hooks() {
+ eval "echo \"\${hooks_${1}}\""
+}
+
+# Fill default anchors for a substage
+# $1: Substage name
+fill_anchors() {
+ eval "${1}=\"${1}_anchor_00 ${1}_anchor_10 ${1}_anchor_20 ${1}_anchor_30 \
+ ${1}_anchor_40 ${1}_anchor_50 ${1}_anchor_60 ${1}_anchor_70 \
+ ${1}_anchor_80 ${1}_anchor_90\""
+}
+
+# Apply a patch to OpenWrt's root
+# $1: Patch file
+apply_patch() {
+ if test -f ${1}; then
+ if test -n "${__use_quilt}"; then
+ exec_log "quilt import \"${1}\""
+ exec_log "quilt push -a"
+ else
+ exec_log "patch -d \"${openwrt_root}\" -p1 -i \"${1}\""
+ fi
+ fi
+}
+
+# Apply patch(es) located in a folder to OpenWrt's root
+# $1: Path to the folder containing patch file(s)
+apply_patches() {
+ if test -d ${1}; then
+ if test -n "${__use_quilt}"; then
+ find "${1}" -name '*.patch' | sort -n | tac | while read line; do
+ exec_log "quilt import \"${line}\""
+ done
+
+ exec_log "quilt push -a"
+ else
+ find "${1}" -name '*.patch' | sort -n | while read line; do
+ apply_patch "${line}" || return $?
+ done
+ fi
+ fi
+}
+
+# Copy a file/folder to OpenWrt's root
+# $1: Path to the file/folder to be copied
+# $2: (Optional) Relative path of OpenWrt root (no / at end)
+copy_file() {
+ if test -f ${1}; then
+ local dest="${openwrt_root}"
+
+ [ -n "${2}" ] && dest="${openwrt_root}/${2}"
+ [ -d "${dest}" ] || exec_log "mkdir -p \"${dest}\""
+ exec_log "cp -af \"${1}\" \"${dest}\""
+ fi
+}
+
+# Copy file(s) from a folder to OpenWrt's root
+# $1: Path to the folder containing file(s)
+# $2: (Optional) Relative path of OpenWrt root (no / at end)
+copy_files() {
+ if test -d "${1}"; then
+ local dest="${openwrt_root}"
+
+ [ -n "${2}" ] && dest="${openwrt_root}/${2}"
+ [ -d "${dest}" ] || exec_log "mkdir -p \"${dest}\""
+ exec_log "cp -af \"${1}\"/* \"${dest}/\""
+ fi
+}
+
+# Prepare a file copy
+# Use this if you need to do some modifications to files to be copied before the actual copying procedure
+# $1: Path to the folder containing file(s)
+# Returns the path of intermediate directory containing files to be copied
+# Use copy_files to finish the copy session
+copy_files_prepare() {
+ local tmpdir=$(mktemp -d -p "${ab_tmp}")
+
+ [ -d "${1}" ] && exec_log "cp -af \"${1}/*\" \"${tmpdir}/\""
+
+ echo "${tmpdir}"
+}
+
+# Remove file/folder
+# $1: Path to the file/folder relative to OpenWrt's root
+remove_file_folder() {
+ exec_log "rm -rf ${openwrt_root}/${1}" || true
+}
+
+# Remove file(s) listed in a file
+# $1: Path to the file lists containing file(s) to be removed
+remove_files_from_list() {
+ if test -f "${1}"; then
+ cat "${1}" | while read line; do
+ if test -n "${line}"; then
+ remove_file_folder "${line}"
+ fi
+ done
+ fi
+}
+
+# Scan valid branch tree and print
+# $1: Parent path
+# $2: Parent branch name
+# $3: Depth
+list_all_autobuild_branches_real() {
+ [ ${3} -gt 4 ] && return
+
+ find ${ab_root}/${1} -maxdepth 2 -mindepth 2 -name 'rules' -print | \
+ awk '{n=split($0,a,"/"); print a[n-1]}' | sort | while read line; do
+
+ local depth=$(($3 + 1))
+ local path=
+ local branch=
+
+ if test -z "${1}"; then
+ path=${line}
+ else
+ path=${1}/${line}
+ fi
+
+ if test -z "${2}"; then
+ branch=${line}
+ else
+ branch=${2}-${line}
+ fi
+
+ print_text "${branch}"
+
+ list_all_autobuild_branches_real "${path}" "${branch}" ${depth}
+ done
+}
+
+# List all valid autobuild branches
+list_all_autobuild_branches() {
+ list_all_autobuild_branches_real "" "" 1
+}
diff --git a/autobuild/unified/scripts/get_openwrt_branch.mk b/autobuild/unified/scripts/get_openwrt_branch.mk
new file mode 100644
index 0000000..78592a3
--- /dev/null
+++ b/autobuild/unified/scripts/get_openwrt_branch.mk
@@ -0,0 +1,4 @@
+include include/version.mk
+
+get-version-number:
+ @echo "$(VERSION_NUMBER)"
diff --git a/autobuild/unified/scripts/get_openwrt_defs.mk b/autobuild/unified/scripts/get_openwrt_defs.mk
new file mode 100644
index 0000000..6a80438
--- /dev/null
+++ b/autobuild/unified/scripts/get_openwrt_defs.mk
@@ -0,0 +1,18 @@
+OPENWRT_BUILD = 1
+
+include Makefile
+
+get-staging-dir-root:
+ @echo "$(STAGING_DIR_ROOT)"
+
+get-bin-dir:
+ @echo "$(BIN_DIR)"
+
+get-build-dir:
+ @echo "$(BUILD_DIR)"
+
+get-target-name:
+ @echo "$(BOARD)"
+
+get-subtarget-name:
+ @echo "$(SUBTARGET)"
diff --git a/autobuild/unified/scripts/get_openwrt_kernel_defs.mk b/autobuild/unified/scripts/get_openwrt_kernel_defs.mk
new file mode 100644
index 0000000..7e8678f
--- /dev/null
+++ b/autobuild/unified/scripts/get_openwrt_kernel_defs.mk
@@ -0,0 +1,15 @@
+OPENWRT_BUILD = 1
+
+__AUTOBUILD_TARGET ?= mediatek
+
+include Makefile
+include target/linux/$(__AUTOBUILD_TARGET)/Makefile
+
+get-target-kernel-ver:
+ @echo "$(KERNEL_PATCHVER)"
+
+get-kernel-build-dir:
+ @echo "$(KERNEL_BUILD_DIR)"
+
+get-linux-build-dir:
+ @echo "$(LINUX_DIR)"
diff --git a/autobuild/unified/scripts/kconfig.sh b/autobuild/unified/scripts/kconfig.sh
new file mode 100755
index 0000000..f379ee2
--- /dev/null
+++ b/autobuild/unified/scripts/kconfig.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Helpers for modifying Kconfig-like files
+
+# Disable config(s) in a Kconfig-like file
+# $1: Kconfig file path
+# $2: List of config name
+kconfig_disable() {
+ local config_file="${1}"
+ local options="${2}"
+ local option=
+
+ [ -z "${config_file}" ] && return
+
+ for option in ${options}; do
+ eval "sed -i '/^${option}=/d' \"${config_file}\""
+ eval "sed -i '/^# ${option} /d' \"${config_file}\""
+ # eval "sed -i '/^# ${option}$/d' \"${config_file}\""
+ echo "# ${option} is not set" >> ${config_file}
+ done
+}
+
+# Enable config(s) in a Kconfig-like file
+# $1: Kconfig file path
+# $2: List of config name
+# $3: Value to be set for config (y if not specified)
+kconfig_enable() {
+ local config_file="${1}"
+ local options="${2}"
+ local value="${3}"
+ local option=
+
+ [ -z "${config_file}" ] && return
+
+ [ -z "${value}" ] && value=y
+
+ for option in ${options}; do
+ eval "sed -i '/^${option}=/d' \"${config_file}\""
+ eval "sed -i '/^# ${option} /d' \"${config_file}\""
+ # eval "sed -i '/^# ${option}\$/d' \"${config_file}\""
+ echo "${option}=${value}" >> ${config_file}
+ done
+}
+
+# Test whether a config is set in a Kconfig-like file
+# Return 0 if set, 1 is not set
+# $1: Kconfig file path
+# $2: Config name
+kconfig_enabled() {
+ local config_file="${1}"
+ local option="${2}"
+
+ [ -z "${config_file}" ] && return 1
+ [ -z "${option}" ] && return 1
+
+ grep "^${option}=" ${config_file} >/dev/null
+}
diff --git a/autobuild/unified/scripts/list.sh b/autobuild/unified/scripts/list.sh
new file mode 100755
index 0000000..25f1fcc
--- /dev/null
+++ b/autobuild/unified/scripts/list.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Helpers for simple list operations
+
+# Check if an element exists in a list
+# $1: list name
+# $2: element name to be checked
+# return 0 if exists, 1 otherwise
+list_find() {
+ local found=
+
+ eval "local list=\"\$${1}\""
+
+ for ele in ${list}; do
+ if test "${ele}" == "${2}"; then
+ found=1
+ fi
+ done
+
+ [ "${found}" = "1" ] && return 0
+
+ return 1
+}
+
+# Delete element(s) from a list
+# $1: list name
+# $2: element name
+list_del() {
+ local tmp_list=
+
+ eval "local list=\"\$${1}\""
+
+ for ele in ${list}; do
+ if test "${ele}" != "${2}"; then
+ tmp_list="${tmp_list} ${ele}"
+ fi
+ done
+
+ eval "${1}=\"\${tmp_list}\""
+}
+
+# Add an element to the end of a list
+# Elements with the same name are allowed
+# $1: list name
+# $2: new element name
+list_append_forced() {
+ eval "${1}=\"\$${1} ${2}\""
+}
+
+# Add an element to the end of a list
+# If the element to be appended already exists, this function does nothing
+# $1: list name
+# $2: new element name
+list_append() {
+ list_find "${1}" "${2}" || list_append_forced "${1}" "${2}"
+}
+
+# Add an element to the end of a list
+# If the element to be appended already exists, this function will remove previous existed element(s)
+# $1: list name
+# $2: new element name
+list_append_unique() {
+ list_find "${1}" "${2}" && list_del "${1}" "${2}"
+ list_append_forced "${1}" "${2}"
+}
+
+# Add an element to the start of a list
+# Elements with the same name are allowed
+# $1: list name
+# $2: new element name
+list_prepend_forced() {
+ eval "${1}=\"${2} \$${1}\""
+}
+
+# Add an element to the start of a list
+# If the element to be prepended already exists, this function does nothing
+# $1: list name
+# $2: new element name
+list_prepend() {
+ list_find "${1}" "${2}" || list_prepend_forced "${1}" "${2}"
+}
+
+# Add an element to the start of a list
+# If the element to be prepended already exists, this function will remove previous existed element(s)
+# $1: list name
+# $2: new element name
+list_prepend_unique() {
+ list_find "${1}" "${2}" && list_del "${1}" "${2}"
+ list_prepend_forced "${1}" "${2}"
+}
+
+# Add an element ahead of an existed element of a list
+# If the target element doesn't exist, this function behaves identical to list_append
+# $1: list name
+# $2: target element name
+# $3: new element name
+list_add_before() {
+ local tmp_list=
+ local added=
+
+ eval "local list=\"\$${1}\""
+
+ for ele in ${list}; do
+ if test -z "${added}" -a "${ele}" == "${2}"; then
+ tmp_list="${tmp_list} ${3}"
+ added=1
+ fi
+
+ tmp_list="${tmp_list} ${ele}"
+ done
+
+ if test "${added}" != "1"; then
+ tmp_list="${tmp_list} ${3}"
+ fi
+
+ eval "${1}=\"\${tmp_list}\""
+}
+
+# Add an element ahead of an existed element of a list
+# If the target element doesn't exist, this function behaves identical to list_append
+# If the target element already exists, this function will remove previous existed element(s)
+# $1: list name
+# $2: target element name
+# $3: new element name
+list_add_before_unique() {
+ list_find "${1}" "${3}" && list_del "${1}" "${3}"
+ list_add_before "${1}" "${2}" "${3}"
+}
+
+# Add an element next to an existed element of a list
+# If the target element doesn't exist, this function behaves identical to list_append
+# $1: list name
+# $2: target element name
+# $3: new element name
+list_add_after() {
+ local tmp_list=
+ local added=
+
+ eval "local list=\"\$${1}\""
+
+ for ele in ${list}; do
+ tmp_list="${tmp_list} ${ele}"
+
+ if test -z "${added}" -a "${ele}" == "${2}"; then
+ tmp_list="${tmp_list} ${3}"
+ added=1
+ fi
+ done
+
+ if test "${added}" != "1"; then
+ tmp_list="${tmp_list} ${3}"
+ fi
+
+ eval "${1}=\"\${tmp_list}\""
+}
+
+# Add an element next to an existed element of a list
+# If the target element doesn't exist, this function behaves identical to list_append
+# If the target element already exists, this function will remove previous existed element(s)
+# $1: list name
+# $2: target element name
+# $3: new element name
+list_add_after_unique() {
+ list_find "${1}" "${3}" && list_del "${1}" "${3}"
+ list_add_after "${1}" "${2}" "${3}"
+}
+
+# Replace element(s) with new one
+# $1: list name
+# $2: target element name
+# $3: new element name
+list_replace() {
+ local tmp_list=
+
+ eval "local list=\"\$${1}\""
+
+ for ele in ${list}; do
+ if test "${ele}" == "${2}"; then
+ tmp_list="${tmp_list} ${3}"
+ else
+ tmp_list="${tmp_list} ${ele}"
+ fi
+ done
+
+ eval "${1}=\"\${tmp_list}\""
+}
diff --git a/autobuild/unified/scripts/log.sh b/autobuild/unified/scripts/log.sh
new file mode 100755
index 0000000..c3f1418
--- /dev/null
+++ b/autobuild/unified/scripts/log.sh
@@ -0,0 +1,177 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Helpers for logging
+
+__log_debug_enabled=
+__log_file_stdout=
+__log_file_stderr=
+__stdout=
+__stderr=
+
+[ -L "/proc/$$/fd/1" ] && __stdout=$(readlink "/proc/$$/fd/1")
+[ -L "/proc/$$/fd/2" ] && __stderr=$(readlink "/proc/$$/fd/2")
+
+# Set file prefix where log will be saved to
+# if $2 == 1, log will be saved to ${2}.log,
+# otherwise stdout and stderr will be saved to ${1}.out.log and ${1}.err.log
+# $1: Log file prefix
+# $2: Whether to merge stdout and stderr into one file (1 for yes, others for no)
+set_log_file_prefix() {
+ if test -z "${1}"; then
+ __log_file_stdout=
+ __log_file_stderr=
+ fi
+
+ if test "x${2}" = x1; then
+ __log_file_stdout="${1}.log"
+ __log_file_stderr="${__log_file_stdout}"
+ else
+ __log_file_stdout="${1}.out.log"
+ __log_file_stderr="${1}.err.log"
+ fi
+
+ rm -f "${__log_file_stdout}" "${__log_file_stderr}"
+}
+
+# Enable logging debug information
+enable_log_debug() {
+ __log_debug_enabled=1
+}
+
+# Save text into log file if possible
+# $1: Message
+__log_text() {
+ if test -n "${__log_file_stdout}"; then
+ echo -e "${1}" >> ${__log_file_stdout}
+ fi
+
+ if test -n "${__log_file_stderr}" -a x"${__log_file_stdout}" != x"${__log_file_stderr}"; then
+ echo -e "${1}" >> ${__log_file_stderr}
+ fi
+}
+
+# Print text to console.
+# if stderr != stdout, print text again for stderr
+# $1: Message
+__con_print_text() {
+ echo -e "${1}"
+
+ if test -n "${__stderr}" -a x"${__stdout}" != x"${__stderr}"; then
+ echo -e "${1}" 1>&2
+ fi
+}
+
+# Print error text without prefix
+# $1: Error message
+log_err_raw() {
+ local text="${1}"
+
+ __con_print_text "\033[93;41m${text}\033[0m" 1>&2
+ __log_text "${text}"
+}
+
+# Print error text
+# $1: Error message
+log_err() {
+ local text="ERROR: ${1}"
+
+ log_err_raw "${text}"
+}
+
+# Print warning text without prefix
+# $1: Warning message
+log_warn_raw() {
+ local text="${1}"
+
+ __con_print_text "\033[1;31m${text}\033[0m"
+ __log_text "${text}"
+}
+
+# Print warning text
+# $1: Warning message
+log_warn() {
+ local text="WARN: ${1}"
+
+ log_warn_raw "${text}"
+}
+
+# Print information text without prefix
+# $1: Information message
+log_info_raw() {
+ local text="${1}"
+
+ __con_print_text "\033[1;36m${text}\033[0m"
+ __log_text "${text}"
+}
+
+# Print information text
+# $1: Information message
+log_info() {
+ local text="INFO: ${1}"
+
+ log_info_raw "${text}"
+}
+
+# Print debugging text
+# $1: Debuging message
+log_dbg() {
+ local text="DEBUG: ${1}"
+
+ [ x"${__log_debug_enabled}" != x"1" ] && return
+
+ __con_print_text "${text}"
+ __log_text "${text}"
+}
+
+# Print stage text
+# $1: Stage message
+prompt_stage() {
+ __con_print_text "\n\033[47;30m${1}\033[0m"
+ __log_text "\n${1}"
+}
+
+# Print config text (stdout only)
+# $1: Config name
+# $2: Config value
+print_conf() {
+ __con_print_text "\033[1m${1}: \033[4m${2}\033[0m"
+ __log_text "${1}: ${2}"
+}
+
+# Print text
+# $1: Text
+print_text() {
+ __con_print_text "${1}"
+ __log_text "${1}"
+}
+
+# Execute command and Save its output to log file(s)
+# $1: Command line
+# $2: No log to file
+# Return the exit code of the command line (may not be accurate for compond commands)
+exec_log() {
+ local ret=
+
+ [ -z "${1}" ] && return
+
+ local expanded=$(eval "echo ${1}")
+ print_text "+ ${expanded}"
+
+ if test -n "${2}"; then
+ eval "${1}"
+ return $?
+ fi
+
+ if test -n "${__log_file_stdout}" -a -n "${__log_file_stderr}"; then
+ eval "{ ${1} 3>&1 1>&2 2>&3 3>&- | tee -a \"${__log_file_stderr}\"; [ \${PIPESTATUS[0]} = 0 ] && true || false; } 3>&1 1>&2 2>&3 3>&- | tee -a \"${__log_file_stdout}\"; ret=\${PIPESTATUS[0]}"
+ elif test -n "${__log_file_stdout}"; then
+ eval "{ ${1} 3>&1 1>&2 2>&3 3>&- | tee -a \"${__log_file_stdout}\"; [ \${PIPESTATUS[0]} = 0 ] && true || false; } 3>&1 1>&2 2>&3 3>&- | tee -a \"${__log_file_stdout}\"; ret=\${PIPESTATUS[0]}"
+ else
+ # No logging required
+ eval "${1}; ret=\$?"
+ fi
+
+ return ${ret}
+}
diff --git a/autobuild/unified/scripts/master/scripts-feeds-support-subdir.patch b/autobuild/unified/scripts/master/scripts-feeds-support-subdir.patch
new file mode 100644
index 0000000..952ef5c
--- /dev/null
+++ b/autobuild/unified/scripts/master/scripts-feeds-support-subdir.patch
@@ -0,0 +1,49 @@
+--- a/scripts/feeds
++++ b/scripts/feeds
+@@ -120,16 +120,22 @@ sub update_location($$)
+ return 0;
+ }
+
+-sub update_index($)
++sub update_index($$)
+ {
+ my $name = shift;
++ my $subdir = shift;
++ my $real_subdir = "";
+
+ -d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
+ -d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1;
+
++ if (length( $subdir || '' )) {
++ $real_subdir = "/$subdir";
++ }
++
+ system("$mk -s prepare-mk OPENWRT_BUILD= TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+- system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+- system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
++ system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name$real_subdir\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
++ system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name$real_subdir\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+ system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index");
+ system("ln -sf $name.tmp/.targetinfo ./feeds/$name.targetindex");
+
+@@ -837,15 +843,17 @@ sub update {
+ my @index_feeds;
+ foreach my $feed (@feeds) {
+ my ($type, $name, $src) = @$feed;
++ my $subdir = $feed->[3]{subdir};
+ next unless $#ARGV == -1 or $opts{a} or $argv_feeds{$name};
+ if (not $opts{i}) {
+ update_feed($type, $name, $src, $opts{f}) == 0 or $failed=1;
+ }
+- push @index_feeds, $name;
++ push @index_feeds, [ $name, $subdir ];
+ }
+- foreach my $name (@index_feeds) {
++ foreach my $index_feed (@index_feeds) {
++ my ($name, $subdir) = @$index_feed;
+ warn "Create index file './feeds/$name.index' \n";
+- update_index($name) == 0 or do {
++ update_index($name, $subdir) == 0 or do {
+ warn "failed.\n";
+ $failed=1;
+ };
diff --git a/autobuild/unified/scripts/openwrt_helpers.sh b/autobuild/unified/scripts/openwrt_helpers.sh
new file mode 100755
index 0000000..86ca6f1
--- /dev/null
+++ b/autobuild/unified/scripts/openwrt_helpers.sh
@@ -0,0 +1,326 @@
+#!/bin/sh
+
+# Copyright (C) 2024 MediaTek Inc. All rights reserved.
+# Author: Weijie Gao <weijie.gao@mediatek.com>
+# Helpers for OpenWrt
+
+# Get OpenWrt's kernel version of a specific target
+# $1: Target name (optional)
+openwrt_get_target_kernel_version() {
+ local target=
+
+ [ -n "${1}" ] && target="__AUTOBUILD_TARGET=${1}"
+
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_kernel_defs.mk" get-target-kernel-ver "${target}"
+}
+
+# Get OpenWrt's linux kernel build directory of a specific target
+# $1: Target name (optional)
+openwrt_get_target_kernel_linux_build_dir() {
+ local target=
+
+ [ -n "${1}" ] && target="__AUTOBUILD_TARGET=${1}"
+
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_kernel_defs.mk" get-linux-build-dir "${target}"
+}
+
+# Get OpenWrt's branch
+openwrt_get_branch() {
+ local version=$(make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_branch.mk" get-version-number)
+
+ if test x"${version}" = x"SNAPSHOT"; then
+ echo "master"
+ return
+ fi
+
+ echo "${version}" | sed 's/\([0-9][0-9]\.[0-9][0-9]\).*/\1/g'
+}
+
+# Get OpenWrt's staging_dir root path
+openwrt_get_staging_dir_root() {
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_defs.mk" get-staging-dir-root
+}
+
+# Get OpenWrt's bin path
+openwrt_get_bin_dir() {
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_defs.mk" get-bin-dir
+}
+
+# Get OpenWrt's target name
+openwrt_get_target_name() {
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_defs.mk" get-target-name
+}
+
+# Get OpenWrt's subtarget name
+openwrt_get_subtarget_name() {
+ make -s -C "${openwrt_root}" -f "${ab_root}/scripts/get_openwrt_defs.mk" get-subtarget-name
+}
+
+# Check if a path is OpenWrt's root directory
+# $1: Path to be checked
+is_openwrt_build_root() {
+ [ -z "${1}" ] && return 1
+
+ [ -d "${1}/include" -a -d "${1}/package" -a -d "${1}/scripts" -a -d "${1}/target" \
+ -a -d "${1}/toolchain" -a -d "${1}/tools" -a -f "${1}/Config.in" -a -f "${1}/Makefile" \
+ -a -f "${1}/rules.mk" -a -f "${1}/scripts/kconfig.pl" ]
+}
+
+# Parse a feed line
+# $1: Line
+# $2: Name of a array to store the contents
+# [0] => (1: disabled; otherwise enabled)
+# [1] => type
+# [2] => flags
+# [3] => name
+# [4] => url
+# [5] => branch/revision/none selection
+# [6] => branch/revision
+__openwrt_feed_line_decompose() {
+ local item_idx=0
+ local arr_idx=0
+ local flag_idx=0
+
+ eval "${2}[0]=" # default enabled
+
+ for item in ${1}; do
+ if test "${item_idx}" -eq 0; then
+ if test x"${item}" = x"#"; then
+ eval "${2}[0]=1" # disabled
+ arr_idx=1
+ elif test x"${item:0:1}" == x"#"; then
+ eval "${2}[0]=1" # disabled
+ eval "${2}[1]=\"${item:1}\"" # type
+ arr_idx=2
+ else
+ eval "${2}[1]=\"${item}\"" # type
+ arr_idx=2
+ fi
+ else
+ if test "${arr_idx}" -eq 1; then
+ eval "${2}[1]=\"${item}\"" # type
+ arr_idx=2
+ elif test "${arr_idx}" -eq 2; then
+ if test x"${item:0:2}" == x"--"; then
+ if test ${flag_idx} -eq 0; then
+ eval "${2}[2]=\"${item}\"" # flags
+ flag_idx=1
+ else
+ eval "${2}[2]=\"\${${2}[2]} ${item}\"" # flags
+ fi
+ else
+ eval "${2}[3]=\"${item}\"" # name
+ arr_idx=4
+ fi
+ elif test "${arr_idx}" -eq 4; then
+ local url_branch=${item%;*}
+ local branch=${item##*;}
+ local url_revision=${item%^*}
+ local revision=${item##*^}
+
+ if test -n "${branch}" -a x"${url_branch}" != x"${branch}"; then
+ eval "${2}[4]=\"${url_branch}\""
+ eval "${2}[5]=\";\""
+ eval "${2}[6]=\"${branch}\""
+ elif test -n "${revision}" -a x"${url_revision}" != x"${revision}"; then
+ eval "${2}[4]=\"${url_revision}\""
+ eval "${2}[5]=\"^\""
+ eval "${2}[6]=\"${revision}\""
+ else
+ eval "${2}[4]=\"${item}\""
+ eval "${2}[5]="
+ eval "${2}[6]="
+ fi
+
+ arr_idx=7
+ break
+ fi
+ fi
+
+ item_idx=1
+ done
+
+ [ "${arr_idx}" -lt 7 ] && return 1
+
+ return 0
+}
+
+# Assemble a feed line
+# $1: Array of the contents
+# Return: Assembled line
+__openwrt_feed_line_compose() {
+ local new_line=
+ local arr=()
+
+ for i in `seq 0 6`; do
+ eval "arr[$i]=\"\${${1}[$i]}\""
+ done
+
+ [ -z "${arr[1]}" -o -z "${arr[3]}" -o -z "${arr[4]}" ] && return 1
+
+ if test x"${arr[0]}" = x"1"; then
+ new_line="# "
+ fi
+
+ new_line="${new_line}${arr[1]} "
+
+ if test -n "${arr[2]}"; then
+ new_line="${new_line}${arr[2]} "
+ fi
+
+ new_line="${new_line}${arr[3]} ${arr[4]}"
+
+ if test x"${arr[5]}" = x";" -o x"${arr[5]}" = x"^"; then
+ if test -n "${arr[6]}"; then
+ new_line="${new_line}${arr[5]}${arr[6]}"
+ fi
+ fi
+
+ echo "${new_line}"
+
+ return 0
+}
+
+# Add new feed to OpenWrt's feeds
+# Existed name-matched lines will be removed
+# $1: Name
+# $2: type
+# $3: URL (Optional with Branch/Revision)
+# $4: (Optional) Flags
+openwrt_feeds_add() {
+ local new_line=
+ local farr=()
+
+ farr[0]=
+ farr[1]="${2}"
+ farr[2]=
+ farr[3]="${1}"
+ farr[4]="${3}"
+ farr[5]=
+ farr[6]=
+
+ shift 3
+ farr[2]="$@"
+
+ new_line=$(__openwrt_feed_line_compose farr)
+
+ openwrt_feeds_remove "${1}"
+
+ echo "${new_line}" >> "${openwrt_root}/feeds.conf.default"
+}
+
+# Replace OpenWrt's feeds repo URL
+# $1: Name
+# $2: New URL
+openwrt_feeds_replace_url() {
+ rm -f "${ab_root}/feeds.conf.mtk"
+
+ cat "${openwrt_root}/feeds.conf.default" | while read line; do
+ local farr=()
+
+ if __openwrt_feed_line_decompose "${line}" farr; then
+ if test x"${farr[3]}" = x"${1}"; then
+ farr[4]="${2}"
+
+ line=$(__openwrt_feed_line_compose farr)
+ fi
+ fi
+
+ echo "${line}" >> "${ab_root}/feeds.conf.mtk"
+ done
+
+ mv "${ab_root}/feeds.conf.mtk" "${openwrt_root}/feeds.conf.default"
+}
+
+# Change OpenWrt's feeds repo git mode
+# $1: Name
+# $2: Set to 1 to use src-git-full, otherwise src-git
+openwrt_feeds_change_src_git_type() {
+ rm -f "${ab_root}/feeds.conf.mtk"
+
+ cat "${openwrt_root}/feeds.conf.default" | while read line; do
+ local farr=()
+
+ if __openwrt_feed_line_decompose "${line}" farr; then
+ if test x"${farr[3]}" = x"${1}"; then
+ if test x"${farr[1]}" = x"src-git" -o x"${farr[1]}" = x"src-git-full"; then
+ if test ${2} -eq 1; then
+ farr[1]="src-git-full"
+ else
+ farr[1]="src-git"
+ fi
+ fi
+
+ line=$(__openwrt_feed_line_compose farr)
+ fi
+ fi
+
+ echo "${line}" >> "${ab_root}/feeds.conf.mtk"
+ done
+
+ mv "${ab_root}/feeds.conf.mtk" "${openwrt_root}/feeds.conf.default"
+}
+
+# Remove OpenWrt's feeds
+# $1: Name
+openwrt_feeds_remove() {
+ rm -f "${ab_root}/feeds.conf.mtk"
+
+ cat "${openwrt_root}/feeds.conf.default" | while read line; do
+ local farr=()
+
+ if __openwrt_feed_line_decompose "${line}" farr; then
+ if test x"${farr[3]}" = x"${1}"; then
+ continue
+ fi
+ fi
+
+ echo "${line}" >> "${ab_root}/feeds.conf.mtk"
+ done
+
+ mv "${ab_root}/feeds.conf.mtk" "${openwrt_root}/feeds.conf.default"
+}
+
+# Disable OpenWrt's feeds
+# $1: Name
+openwrt_feeds_disable() {
+ rm -f "${ab_root}/feeds.conf.mtk"
+
+ cat "${openwrt_root}/feeds.conf.default" | while read line; do
+ local farr=()
+
+ if __openwrt_feed_line_decompose "${line}" farr; then
+ if test x"${farr[3]}" = x"${1}" -a x"${farr[0]}" != x"1"; then
+ farr[0]=1
+
+ line=$(__openwrt_feed_line_compose farr)
+ fi
+ fi
+
+ echo "${line}" >> "${ab_root}/feeds.conf.mtk"
+ done
+
+ mv "${ab_root}/feeds.conf.mtk" "${openwrt_root}/feeds.conf.default"
+}
+
+# Enable OpenWrt's feeds
+# $1: Name
+openwrt_feeds_enable() {
+ rm -f "${ab_root}/feeds.conf.mtk"
+
+ cat "${openwrt_root}/feeds.conf.default" | while read line; do
+ local farr=()
+
+ if __openwrt_feed_line_decompose "${line}" farr; then
+ if test x"${farr[3]}" = x"${1}" -a x"${farr[0]}" = x"1"; then
+ farr[0]=
+
+ line=$(__openwrt_feed_line_compose farr)
+ fi
+ fi
+
+ echo "${line}" >> "${ab_root}/feeds.conf.mtk"
+ done
+
+ mv "${ab_root}/feeds.conf.mtk" "${openwrt_root}/feeds.conf.default"
+}
diff --git a/autobuild/unified/scripts/openwrt_kconfig.mk b/autobuild/unified/scripts/openwrt_kconfig.mk
new file mode 100644
index 0000000..4c1e930
--- /dev/null
+++ b/autobuild/unified/scripts/openwrt_kconfig.mk
@@ -0,0 +1,20 @@
+
+include Makefile
+
+savedefconfig: scripts/config/conf prepare-tmpinfo FORCE
+ [ -e .config ] && { \
+ $< --$@=$(if $(CONFIG_FILE),$(CONFIG_FILE),defconfig) Config.in && \
+ printf "Default config file saved to defconfig\n"; \
+ } || { \
+ printf ".config not exist!\n" >&2; \
+ false; \
+ }
+
+loaddefconfig: scripts/config/conf prepare-tmpinfo FORCE
+ [ -e "$(CONFIG_FILE)" ] && { \
+ [ -L .config ] && export KCONFIG_OVERWRITECONFIG=1; \
+ $< --defconfig=$(CONFIG_FILE) Config.in; \
+ } || { \
+ printf "Default config file not specified by CONFIG_FILE= !\n" >&2; \
+ false; \
+ }