学习韦东山视频uboot之Makefile分析

rules.mk文件内容

_depend:    $(obj).depend

$(obj).depend:    $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
        @rm -f $@
        @for f in $(SRCS); do \
            g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
            $(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
        done


        
我觉得,要看懂这些,需要在一开始把所有的变量都记录下来,后面再把所有的变量给展开,就好懂
很多了。

上面文件内容最难懂的一句是:

g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \

这里大概就是说,把basename $$f 得到的结果送到sed执行。
而sed的作用是把$$f的后缀(.xxx)改成(.o)

所以,rules.mk的内容就是:
为了生成_depend,需要依赖$(obj).depend
而为了生成$(obj).depend,需要依赖$(src)Makefile $(TOPDIR)/config.mk $(SRCS)
首先删除已有的目标
然后遍历SRCS(估计是每一个文件夹)
然后执行
把所有的x.x改成x.o
然后赋值给g
最后运行

$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(OBJ)$$g $$f >> $@

我这里不是很明白$$是啥意思,从执行来看,就是个双指针,但是为什么这么做呢?
难道跟for语句有关?分析一下shell中的for语句先。
果然是这样,因为是数组,所以要用两个$$才能拿到变量值
所以就是说

$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(OBJ)xxx.o xxx.x >> $@

这一句有2两个详细关键字,分别是
basename 和sed
关于命令跳到下面看,这里只看上面一句的意思,所以,首先把$$f变量的目录去掉,剩下文件名
传送给sed命令
分解一下$$f,首先f是一个变量,第一步获取$f,然后这个$f再作为一个变量,有点双指针的意思。

然后是:
sed -e 's/\(.*\)\.\w/\1.o/'basename 命令:返回一个字符串参数的基本文件名称
再terminal中测试一下 basename 命令得到一下结果

$ basename //hello/hello.c
hello.c

$ basename --help
Usage: basename NAME [SUFFIX]
  or:  basename OPTION... NAME...
Print NAME with any leading directory components removed.
If specified, also remove a trailing SUFFIX.

Mandatory arguments to long options are mandatory for short options too.
  -a, --multiple       support multiple arguments and treat each as a NAME
  -s, --suffix=SUFFIX  remove a trailing SUFFIX; implies -a
  -z, --zero           end each output line with NUL, not newline
      --help     display this help and exit
      --version  output version information and exit

Examples:
  basename /usr/bin/sort          -> "sort"
  basename include/stdio.h .h     -> "stdio"
  basename -s .h include/stdio.h  -> "stdio"
  basename -a any/str1 any/str2   -> "str1" followed by "str2"

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/basename>
or available locally via: info '(coreutils) basename invocation'

翻译一下上面的help意思
首先是语法:
basename NAME [SUFFIX]
或者是
basename OPTION... NAME...

意思:把NAME中的前导目录组件移除后再打印,如果如果有规定SUFFIX部分,同样需要移除掉
简单来说就是移除掉目录和SUFFIX指定的部分得到剩下的东西并返回
具体怎么用可以看Examples,简单直接sed命令:

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

  -n, --quiet, --silent
                 suppress automatic printing of pattern space
  -e script, --expression=script
                 add the script to the commands to be executed
  -f script-file, --file=script-file
                 add the contents of script-file to the commands to be executed
  --follow-symlinks
                 follow symlinks when processing in place
  -i[SUFFIX], --in-place[=SUFFIX]
                 edit files in place (makes backup if SUFFIX supplied)
  -l N, --line-length=N
                 specify the desired line-wrap length for the `l' command
  --posix
                 disable all GNU extensions.
  -r, --regexp-extended
                 use extended regular expressions in the script.
  -s, --separate
                 consider files as separate rather than as a single continuous
                 long stream.
  -u, --unbuffered
                 load minimal amounts of data from the input files and flush
                 the output buffers more often
  -z, --null-data
                 separate lines by NUL characters
      --help     display this help and exit
      --version  output version information and exit

If no -e, --expression, -f, or --file option is given, then the first
non-option argument is taken as the sed script to interpret.  All
remaining arguments are names of input files; if no input files are
specified, then the standard input is read.

GNU sed home page: <http://www.gnu.org/software/sed/>.
General help using GNU software: <http://www.gnu.org/gethelp/>.


首先sed是一种流编辑器,可以搜索,可以替换,用这个命令前需要掌握一点正则表达式的基础。
用法:sed [OPTION]... {script-only-if-no-other-script} [input-file]...
以分析目标举例
sed -e 's/\(.*\)\.\w/\1.o/'
-e就是script
add the script to the commands to be executed
把脚本添加到命令中去并执行
大概就是说,按照这个正则表达式去匹配输入内容得到匹配的结果把。
s表示替换
所以把输入的字符串中符合‘\(.*\)\.\w’的替换成\1.o
首先用正则表达式来分解‘\(.*\)\.\w’
首先,把sed的中的字符集给移除掉。
sed中,\(..\)表示匹配字串,保存匹配的字符
所以,把\(xxx\)中的xxx保存到\1中
分为3部分
.*
开头以'('结尾以')'的任意除了换行符以外的所有字符
\.
\w
\. 表示匹配一个'.'字符
\w    匹配任何单个 "单词" 字符, 即字母, 数字或下划线. 这等同于 [a-zA-Z0-9_]. 
相反地, 大写的 \W 表示 "任何 非单词字符".
所以整个句子的意思就是,就是把a.c中的.c换成.o
例如main.c 变成 main.o
/
/
分析Makefile
编译uboot时,第一步是配置
make 100ask24x0_config
在Makefile中,可以查到这个目标:
 

100ask24x0_config    :    unconfig
    @$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

MKCONFIG    := $(SRCTREE)/mkconfig
export MKCONFIG
SRCTREE        := $(CURDIR)

化简就是:

100ask24x0_config    :    unconfig
    mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

mkconfig文件注释如下
APPEND=no    # Default: Create new config file
BOARD_NAME=""    # Name to print in make output

mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

-gt  表示 大于
现在有6个,OK

$1 = 100ask24x0
没有--
没有-a
没有-n
所以跳过

while [ $# -gt 0 ] ; do
    case "$1" in
    --) shift ; break ;;
    -a) shift ; APPEND=yes ;;
    -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
    *)  break ;;
    esac
done


||表示条件如果为假则执行后面的语句,引申&&表示条件如果为真则执行后面的语句
由于BOARD_NAME = NULL,所以执行后面的语句
BOARD_NAME = 100ask24x0
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"

参数个数如果小于4或者大于6都退出,现在刚好6个,跳过

[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1

打印    "Configuring for ${BOARD_NAME} board..."

echo "Configuring for 100ask24x0 board..."

#
# Create link to architecture specific headers
#
如果SRCTREE不等于OBJTREE,执行then后面,否则执行else后面语句
分析Makefile开头时,

SRCTREE        := $(CURDIR)
OBJTREE        := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

OBJTREE表示如果BUILD_DIR存在则是BUILD_DIR,否则是CURDIR

截取Makefile中的BUILD_DIR语句
如果定义了O,则行下面语句,所以跳过

ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif

如果BUILD_DIR != NULL,执行下面的语句,所以也跳过

ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)

# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)

所以SRCTREE和OBJTREE都是NULL
由于SRCTREE和OBJTREE都是NULL,所以执行else后面的语句

if [ "$SRCTREE" != "$OBJTREE" ] ; then
    mkdir -p ${OBJTREE}/include
    mkdir -p ${OBJTREE}/include2
    cd ${OBJTREE}/include2
    rm -f asm
    ln -s ${SRCTREE}/include/asm-$2 asm
    LNPREFIX="../../include2/asm/"
    cd ../include
    rm -rf asm-$2
    rm -f asm
    mkdir asm-$2
    ln -s asm-$2 asm
else
    #进入include文件夹,删除asm,然后建立一个连接文件
    cd ./include
    rm -f asm
    ln -s asm-$2 asm
    #上面表示:ln -s asm-arm asm
fi

删除 asm-arm/arch文件夹
rm -f asm-$2/arch

$6长度为0或者%6为NULL执行then后面语句,否则则行else后面语句
$6 = s3c24x0,所以执行else后面语句

if [ -z "$6" -o "$6" = "NULL" ] ; then
    ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
    ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi

if [ "$2" = "arm" ] ; then
    #删除 asm-arm/proc
    rm -f asm-$2/proc
    #LNPREFIX为NULL所以下面简化为:ln -s proc-armv asm-arm/proc
    ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi

#
# Create include file for Make
#
echo "ARCH   = $2" >  config.mk
echo "CPU    = $3" >> config.mk
echo "BOARD  = $4" >> config.mk

[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk

[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk


输入以上参数到config.mk, >表示新建, >>表示追加,所以config的内容是:
ARCH    = arm
CPU        = arm920t
BOARD     = 100ask24x0
$5不为空且$5不为NULL执行后面,但是$5为NULL,所以跳过
同样,$6=s3c24x0,所以追加,最后config.mk的内容就是
ARCH    = arm
CPU        = arm920t
BOARD     = 100ask24x0
SOC     = s3c24x0    


APPEND为NO,执行else,新建一个config.h文件

#
# Create board specific header file
#
if [ "$APPEND" = "yes" ]    # Append to existing config file
then
    echo >> config.h
else
    > config.h        # Create new config file
fi


然后追加下面内容到config.h文件里

echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

所以config.h的内容是:

/* Automatically generated - do not edit */
#include <configs/100ask24x0.h>

最后完成退出
exit 0

//
配置完,执行make
all:        $(ALL)
all依赖ALL
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
以下是Makefile的$(obj)内容,所以obj为空

# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src

ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif

如果CONFIG_NAND_U_BOOT == yes
但是Makefile中没有,所以为空所以上面命令化简为:

all:        u-boot.srec u-boot.bin System.map $(U_BOOT_NAND)


u-boot.srec:    u-boot
        $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

u-boot.bin:    $(obj)u-boot
        $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

u-boot:        depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
        UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
        cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
            --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
            -Map u-boot.map -o u-boot

System.map:    u-boot
        @$(NM) $< | \
        grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
        sort > $(obj)System.map

上面把变量删除得到的
所以我们的目标得到的u-boot.bin是通过u-boot得到的

u-boot:        depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
        UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
        cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
            --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
            -Map u-boot.map -o u-boot
            


所以make主要执行了这个

UNDEF_SYM=`arm-linux-objdump -x \
    lib_generic/libgeneric.a \
    board/100ask24x0/lib100ask24x0.a \
    cpu/arm920t/libarm920t.a \
    cpu/arm920t/s3c24x0/libs3c24x0.a \
    lib_arm/libarm.a \
    fs/cramfs/libcramfs.a \
    fs/fat/libfat.a \
    fs/fdos/libfdos.a \
    fs/jffs2/libjffs2.a \
    fs/reiserfs/libreiserfs.a \
    fs/ext2/libext2fs.a \
    net/libnet.a \
    disk/libdisk.a \
    rtc/librtc.a \
    dtt/libdtt.a \
    drivers/libdrivers.a \
    drivers/nand/libnand.a \
    drivers/nand_legacy/libnand_legacy.a \
    drivers/usb/libusb.a \
    drivers/sk98lin/libsk98lin.a \
    common/libcommon.a \
    |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
    cd /home/inc/work/uboot_s3c2440/u-boot-1.1.6 && arm-linux-ld -Bstatic -T \
    /home/inc/work/uboot_s3c2440/u-boot-1.1.6/board/100ask24x0/u-boot.lds \
    -Ttext 0x33F80000  \
    $UNDEF_SYM cpu/arm920t/start.o \
        --start-group \
        lib_generic/libgeneric.a \
        board/100ask24x0/lib100ask24x0.a \
        cpu/arm920t/libarm920t.a \
        cpu/arm920t/s3c24x0/libs3c24x0.a \
        lib_arm/libarm.a \
        fs/cramfs/libcramfs.a \
        fs/fat/libfat.a \
        fs/fdos/libfdos.a \
        fs/jffs2/libjffs2.a \
        fs/reiserfs/libreiserfs.a \
        fs/ext2/libext2fs.a \
        net/libnet.a \
        disk/libdisk.a \
        rtc/librtc.a \
        dtt/libdtt.a \
        drivers/libdrivers.a \
        drivers/nand/libnand.a \
        drivers/nand_legacy/libnand_legacy.a \
        drivers/usb/libusb.a \
        drivers/sk98lin/libsk98lin.a \
        common/libcommon.a \
        --end-group 
        -L /home/inc/work/tool_s3c2440/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5 -lgcc \
        -Map u-boot.map -o u-boot


从上面命令可以看出,怎么组织代码,主要看连接脚本 u-boot.lds
在这里,后面存在一个:-Ttext = 0x33F80000

从连接文件可以得到,第一个文件是start.o,然后是boot_init.o
{
    第一个文件是start.o,然后是100ask24x0/boot_init.o
  cpu/arm920t/start.o    (.text)
      board/100ask24x0/boot_init.o (.text)
  *(.text)
}
所以分析源码先从这两个文件开始分析起来


 

### Android 校园实验室设备管理系统开发实现方案 #### 1. 系统架构设计 该系统旨在提供一种高效便捷的方式来管理和预约校园内的实验室及其设备。整个系统由前端应用和后端服务组成,其中前端部分针对Android手机和平板电脑优化,而后端则负责数据存储、业务逻辑处理和服务接口。 - **前后端分离模式**:采用RESTful API作为通信协议,确保客户端与服务器之间的解耦合[^1]。 - **技术栈选择** - 后端选用Spring Boot框架构建微服务架构的服务层,利用其内置的安全机制来保护API免受未授权访问的影响。 - 数据库方面可以选择MySQL或其他关系型数据库保存结构化数据,如用户资料、设备清单等;对于非结构化的文件上传下载操作,则考虑使用NoSQL解决方案,比如MongoDB或云储存服务。 - 缓存层引入Redis提升查询效率并减轻数据库压力。 - 客户端基于Kotlin/Java语言编写原生APP界面,并集成Retrofit进行HTTP请求封装,简化网络交互流程。 #### 2. 功能模块划分 根据实际应用场景的需求分析,可以将此项目划分为以下几个核心子系统: ##### 用户认证与权限控制 实现登录注册功能的同时还要兼顾多角色身份验证(如管理员、老师、学生),并通过OAuth2.0协议增强安全性[^2]。 ##### 实验室信息展示 允许浏览者查看各个实验室的具体位置、开放时间表以及当前可用座位数等内容。这部分内容可以通过地图插件直观呈现给访客。 ##### 预约申请提交 当有空闲时段时,符合条件的学生即可发起新的预订请求。此时需填写必要的个人信息及所选时间段,并等待相关人员审批确认。 ##### 设备借用管理 除了基本的借还记录外,还需支持在线续租延长使用权期限的功能。另外就是关于损坏赔偿事宜也要有所规划,例如设置固定金额罚款或是按比例收取维修费用等方式。 ##### 维护工单跟踪 一旦发现仪器故障问题,使用者应当能够迅速提报至后台待办事项列表里边去。之后再由技术人员接手解决直至恢复正常运作为止。 ##### 新闻资讯推送 定期更新校内外动态消息,促进师生间交流互动。同时也可以用来发布公告通知重要事件提醒大家注意查收阅读。 ```kotlin // 示例代码片段:定义一个简单的实体类表示实验室基本信息 data class LabInfo( val id: Int, val name: String, val location: String, val capacity: Int, var isOpened: Boolean = false ) fun main() { println("Welcome to the Laboratory Management System!") } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值