1747|0

155

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【Luckfox幸狐 RV1106 Linux 开发板测评】十六、build.sh源码分析 [复制链接]

 

本人【Luckfox幸狐 RV1106 Linux 开发板测评】帖子链接:

一、开箱及测试

二、SDK获取与编译镜像

三、GPIO点灯

四、通过PC机共享网络接入Internet和Ubuntu下Python点灯

五、编译Buildroot系统并测试摄像头

六、PWM控制——命令和C方式

七、PWM控制——Python和设备树方式

八、ADC和UART测试

九、Python控制I2C驱动OLED

十、C程序控制I2C驱动OLED

十一、SPI驱动LCD

十二、实现FrameBuffer设备及LVGL应用

十三、五向开关作为LVGL输入设备

十四、LVGL显示DHT11的温湿度 

十五、MPU6050的内核驱动

 

出于对Luckfox SDK进行内核构建所用build.sh脚本的好奇(手里还有一个吃灰的RK3568板子,最近发现它的SDK也是利用了build.sh,猜想是瑞芯微原厂方案),近期花时间把它的源码整个分析了一下,记录于本篇测评报告。注:分析过程利用了文心一言结合自己的理解,若有错误欢迎大家指正。

1、开篇部分的变量定义和导出

build.sh直接运行的代码集中在文件开篇和结尾部分,中间大段篇幅则是bash函数的定义。而开篇部分代码主要是各种变量的定义和导出,比如下述是build.sh的前14行代码:

#!/bin/bash
set -eE

export LC_ALL=C
export LD_LIBRARY_PATH=

function unset_env_config_rk()
{
  local tmp_file=`mktemp`
  env | grep -oh "^RK_.*=" > $tmp_file || true
  source $tmp_file
  rm -f $tmp_file
}
unset_env_config_rk
##############################################################################
# 总结,上述代码主要工作:

# 1. "set -eE"设置脚本在遇到错误时退出:
#   -e: 如果任何语句的执行结果不是true,则退出bash;
#   -E: 当脚本成功完成时,其退出状态是0,非零值通常表示某种错误。
# 2. "export LC_ALL=C"设置了语言环境(C语言)以避免本地化差异。
# 3. "export LD_LIBRARY_PATH="清空了LD_LIBRARY_PATH环境变量。
# 4. 定义了一个函数"unset_env_config_rk",并调用:
#   该函数从当前环境变量中筛选出所有以RK_开头的变量;
#   将RK_开头变量存放在临时文件mktemp中,
#   source临时文件,即设置其中的RK_开头变量成为环境变量它们;
#   删除临时文件(实际测试一般没有RK_开题变量)。
##############################################################################

接着,利用realpath和dirname命令获取build.sh文件自身所在的路径,进而得到Luckfox SDK的绝对路径了。本人也是在分析到这里时发现了SDK根目录上的build.sh实际是一个软链接,指向<SDK>/project/build.sh。

##############################################################################
# Global Variable Configure
##############################################################################
_FDS="\\ \n"                                # _DFS:“反斜杠+回车”即字符串转换拼贴
cmd=`realpath $0`                           # realpath:本文件($0表示)的绝对路径
COMMON_DIR=`dirname $cmd`                   # dirname:路径部分即不包含脚本文件名
# 实际:COMMON_DIR=<SDK>/project/
# 根目录build.sh是一个软链接,指向project/build.sh
PROJECT_TOP_DIR=$(realpath $COMMON_DIR/)    # 指向<SDK>/project
SDK_ROOT_DIR=$(realpath $COMMON_DIR/..)     # COMMON_DIR父目录即SDK根目录绝对路径
SDK_SYSDRV_DIR=${SDK_ROOT_DIR}/sysdrv       # 指向<SDK>/sysdrv
SDK_MEDIA_DIR=${SDK_ROOT_DIR}/media         # 指向<SDK>/media
SDK_APP_DIR=${PROJECT_TOP_DIR}/app          # 指向<SDK>/project/app
BOARD_CONFIG=$SDK_ROOT_DIR/.BoardConfig.mk  # BOARD_CONFIG指向.BoardConfig.mk
TARGET_PRODUCT_DIR=${PROJECT_TOP_DIR}/cfg   # 指向<SDK>/project/cfg
GLOBAL_ROOT_FILESYSTEM_NAME=rootfs          # 根文件系统名称
GLOBAL_OEM_NAME=oem                         # oem名称
GLOBAL_FS_TYPE_SUFFIX=_fs_type              # 文件系统类型后缀
GLOBAL_INITRAMFS_BOOT_NAME=""               # initramfs的启动名称,此处空
GLOBAL_PARTITIONS=""                        # 分区信息,此处空
GLOBAL_SDK_VERSION=""                       # SDK版本,此处空

######################################################
# if语句检查当前系统的CPU核心数:
# 如果只有一个核心在线(getconf _NPROCESSORS_ONLN返回1)
# 则RK_JOBS被设置为1
# 如果有多个核心在线,则RK_JOBS被设置为在线核心数减1。
# 这个变量通常用于make命令的-j选项,以并行地执行构建任务。
######################################################
if [ `getconf _NPROCESSORS_ONLN` -eq 1 ]; then
  export RK_JOBS=1
else
  export RK_JOBS=$((`getconf _NPROCESSORS_ONLN` - 1 ))
fi

export RK_BUILD_VERSION_TYPE=RELEASE # 构建版本类型RELEASE

######################################################
# SDK_ROOT_DIR:再次导出SDK的根目录路径。
# RK_PROJECT_OUTPUT:设置项目输出的根目录。
# RK_PROJECT_TOP_DIR:设置项目顶级目录。
# RK_PROJECT_PATH_MEDIA、RK_PROJECT_PATH_SYSDRV、
#   RK_PROJECT_PATH_APP:设置项目不同部分的输出目录。
# RK_PROJECT_OUTPUT_IMAGE:设置输出镜像的目录。
# RK_PROJECT_PATH_RAMDISK、RK_PROJECT_PATH_FASTBOOT等:
#   设置其他特定输出目录。
# PATH:添加RK_PROJECT_PATH_PC_TOOLS到PATH中,
#   这样在执行命令时可以直接访问该目录下的工具。
# RK_PROJECT_FILE_ROOTFS_SCRIPT、
#   RK_PROJECT_FILE_OEM_SCRIPT:设置不同脚本文件的路径。
# RK_PROJECT_TOOLS_MKFS_*:
#   设置创建不同文件系统所需工具的脚本路径。
######################################################
export SDK_ROOT_DIR=$SDK_ROOT_DIR
export RK_PROJECT_OUTPUT=$SDK_ROOT_DIR/output/out
export RK_PROJECT_TOP_DIR=$PROJECT_TOP_DIR
export RK_PROJECT_PATH_MEDIA=$SDK_ROOT_DIR/output/out/media_out
export RK_PROJECT_PATH_SYSDRV=$SDK_ROOT_DIR/output/out/sysdrv_out
export RK_PROJECT_PATH_APP=$SDK_ROOT_DIR/output/out/app_out
export RK_PROJECT_PATH_PC_TOOLS=$SDK_ROOT_DIR/output/out/sysdrv_out/pc
export RK_PROJECT_OUTPUT_IMAGE=$SDK_ROOT_DIR/output/image
export RK_PROJECT_PATH_RAMDISK=$SDK_ROOT_DIR/output/out/ramdisk
export RK_PROJECT_PATH_FASTBOOT=$SDK_ROOT_DIR/output/out/fastboot
export RK_PROJECT_PATH_RAMDISK_TINY_ROOTFS=$RK_PROJECT_PATH_RAMDISK/tiny_rootfs

export PATH=$RK_PROJECT_PATH_PC_TOOLS:$PATH

export RK_PROJECT_FILE_ROOTFS_SCRIPT=$RK_PROJECT_OUTPUT/S20linkmount
export RK_PROJECT_FILE_OEM_SCRIPT=$RK_PROJECT_OUTPUT/S21appinit
export RK_PROJECT_FILE_RECOVERY_SCRIPT=$RK_PROJECT_PATH_RAMDISK_TINY_ROOTFS/etc/init.d/S10linkdev
export RK_PROJECT_FILE_RECOVERY_LUNCH_SCRIPT=$RK_PROJECT_PATH_RAMDISK_TINY_ROOTFS/etc/init.d/S99lunch_recovery
export RK_PROJECT_TOOLS_MKFS_SQUASHFS=mkfs_squashfs.sh
export RK_PROJECT_TOOLS_MKFS_EXT4=mkfs_ext4.sh
export RK_PROJECT_TOOLS_MKFS_UBIFS=mkfs_ubi.sh
export RK_PROJECT_TOOLS_MKFS_JFFS2=mkfs_jffs2.sh
export RK_PROJECT_TOOLS_MKFS_ROMFS=mkfs_romfs.sh
export RK_PROJECT_TOOLS_MKFS_EROFS=mkfs_erofs.sh
export RK_PROJECT_TOOLS_MKFS_INITRAMFS=mkfs_initramfs.sh

######################################################
# RK_PROJECT_ROOTFS_TYPE:可能根文件系统的类型。
# OTA_SCRIPT_PATH:设置OTA(Over-The-Air)脚本的路径。
# ENV_CFG_FILE:设置环境配置文件的路径。
# ENV_SIZE 和 ENV_OFFSET:可能存储环境变量的大小和偏移量。
######################################################
RK_PROJECT_ROOTFS_TYPE=""
OTA_SCRIPT_PATH=$RK_PROJECT_PATH_RAMDISK
ENV_CFG_FILE=$RK_PROJECT_OUTPUT_IMAGE/.env.txt
ENV_SIZE=""
ENV_OFFSET=""

2、结尾部分的实际业务逻辑代码

build.sh结尾部分是实际的业务逻辑,也就是执行:build.sh + 参数项的执行代码:

#=========================
# build targets
#=========================
# 设置了当脚本中任何命令返回非零退出状态时执行的错误处理函数err_handler。
trap 'err_handler' ERR
# 切换到脚本定义的$PROJECT_TOP_DIR目录,即<SDK>/project
cd $PROJECT_TOP_DIR
# 调用函数:从当前 shell 环境中移除所有与 RK_ 前缀相关的环境变量,
#   这些环境变量通常是在 BoardConfig*.mk 文件中通过 export 语句定义的。
unset_board_config_all
# ./build.sh lunch即sh脚本执行时第一个参数为lunch,则执行build_select_board LUNCH-FORCE
if [ "$1" = "lunch" ];then
  # 选择并设置目标开发板的构建配置(见260行),即选择开发板更新defconfig文件
  build_select_board LUNCH-FORCE
fi
# 如果$BOARD_CONFIG指定的配置文件不存在,则调用build_select_board函数。
#   即从未执行./build.sh lunch,那么需要强制进入设置目标开发板的阶段
if [ ! -e "$BOARD_CONFIG" ];then
  build_select_board
fi
# 如果$BOARD_CONFIG是一个符号链接,则执行source命令来加载它,用于导入环境变量或函数定义。
#   BOARD_CONFIG指向.BoardConfig.mk,即./build.sh lunch得到的开发板配置文件
[ -L "$BOARD_CONFIG" ] && source $BOARD_CONFIG
# 设置交叉编译工具链和将其路径添加到PATH环境变量中。
#   (之前一直好奇Luckfox编译镜像不要设置环境变量,因为这里做了设置)
export RK_PROJECT_TOOLCHAIN_CROSS=$RK_TOOLCHAIN_CROSS
export PATH="${SDK_ROOT_DIR}/tools/linux/toolchain/${RK_PROJECT_TOOLCHAIN_CROSS}/bin":$PATH

# 如果目标根文件系统是ubuntu,则根据LF_SUBMODULES_BY的值来复制对应的.gitmodules文件,并更新Git子模块。
if [[ "$LF_TARGET_ROOTFS" = "ubuntu" ]];then
    if [[ "$LF_SUBMODULES_BY" = "github" ]];then
        cp ${SDK_ROOT_DIR}/.gitmodules.github ${SDK_ROOT_DIR}/.gitmodules
    else
        if [[ "$LF_SUBMODULES_BY" = "gitee" ]];then
            cp ${SDK_ROOT_DIR}/.gitmodules.gitee ${SDK_ROOT_DIR}/.gitmodules
        else
            exit 0
        fi
    fi
    git submodule update --init --recursive
fi

# 如果命令行参数中包含help或-h,则显示帮助信息。
if echo $@|grep -wqE "help|-h"; then
  # 应该是分析是否有第2参数,若有则进一步调用usage$2来输出子参数信息。
  #   比如:./build.sh help exec,那么就会输出exec的帮助信息。
  #   后经实际测试,二级信息输出只支持:media、sysdrv、kernel、uboot、rootfs
  #   即有函数usagemedia等。
  if [ -n "$2" -a "$(type -t usage$2)" == function ]; then
    echo "###Current Configure [ $2 ] Build Command###"
    eval usage$2
  else
    usage
  fi
  exit 0
fi

# 检查指定的交叉编译gcc是否存在,如果不存在则输出错误信息并退出脚本。
if ! ${RK_PROJECT_TOOLCHAIN_CROSS}-gcc --version &> /dev/null; then
  msg_error "Not found toolchain ${RK_PROJECT_TOOLCHAIN_CROSS}-gcc for [$RK_CHIP] !!!"
  msg_info "Please run these commands to install ${RK_PROJECT_TOOLCHAIN_CROSS}-gcc"
  echo ""
  echo "    cd ${SDK_ROOT_DIR}/tools/linux/toolchain/${RK_PROJECT_TOOLCHAIN_CROSS}/"
  echo "    source env_install_toolchain.sh"
  echo ""
  exit 1
fi

# 根据RK_PROJECT_TOOLCHAIN_CROSS的值设置库类型(uclibc或glibc)。
#   看来Luckfox基于RV1106应该是导入glibc库了。
case $RK_PROJECT_TOOLCHAIN_CROSS in
  arm-rockchip830-linux-uclibcgnueabihf)
  export RK_LIBC_TPYE=uclibc
  ;;
  *)
  export RK_LIBC_TPYE=glibc
  ;;
esac
# 设置构建过程中使用的各种输出目录路径。
export RK_PROJECT_PACKAGE_ROOTFS_DIR=$RK_PROJECT_OUTPUT/rootfs_${RK_LIBC_TPYE}_${RK_CHIP}
export RK_PROJECT_PACKAGE_OEM_DIR=$RK_PROJECT_OUTPUT/oem
export RK_PROJECT_PACKAGE_USERDATA_DIR=$RK_PROJECT_OUTPUT/userdata
export RK_PROJECT_PATH_BOARD_BIN=$RK_PROJECT_PATH_SYSDRV/board_${RK_LIBC_TPYE}_${RK_CHIP}

# 调用build_check和__PREPARE_BOARD_CFG函数进行构建前的检查和准备板级配置。
build_check
__PREPARE_BOARD_CFG

# $#传递当前脚本(即./build.sh)运行的输入参数个数,这个数值存在num中。
num=$#
option=""

# 最终的build.sh执行逻辑
#   脚本命令包含参数1,则根据参数执行不同的功能(前面定义的函数)。
#   不同的功能函数实际先存储在变量option中。
#   其中clean需要参数2,以确定clean的部分。
while [ $# -ne 0 ]
do
  case $1 in
    DEBUG) export RK_BUILD_VERSION_TYPE=DEBUG;;
    all) option=build_all ;;
    save) option=build_save ;;
    allsave) option=build_allsave ;;
    check) option=build_check ;;
    clean) option="build_clean $2";break;;
    firmware) option=build_firmware ;;
    ota) option=build_ota ;;
    updateimg) option=build_updateimg ;;
    unpackimg) option=build_unpack_updateimg ;;
    factory) option=build_factory ;;
    recovery) option=build_recovery ;;
    env) option=build_env ;;
    meta) option=build_meta ;;
    driver) option=build_driver ;;
    sysdrv) option=build_sysdrv ;;
    uboot) option=build_uboot ;;
    kernel) option=build_kernel ;;
    rootfs) option=build_rootfs ;;
    media) option=build_media ;;
    app) option=build_app ;;
    info) option=build_info ;;
    tool) option=build_tool ;;
    *) option=usage ;;
  esac
  if [ $((num)) -gt 0 ]; then
    shift
  fi
done
#   最后利用eval命令执行option指向的函数功能
eval "${option:-build_allsave}"

 

上述代码先判断是否执行了“build.sh lunch”命令,进而实现开发板选型的逻辑。最后的while循环决定了build.sh可以附带的其它命令参数,比如本人实际使用过的:“build.sh clean”、“build.sh kernel”等。而这些命令会通过调用函数来实现,比如build_all()、build_clean()等,这些函数的定义代码也就是在文件的中间部分,限于篇幅这里不做介绍了,附上本人添加了注释后的build.sh文件(由于时间关系仅注释了一半的函数定义)。

 

build.sh (78.04 KB, 下载次数: 7)



 

 

点赞(2) 关注(2)
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表