【Linux】制作U-Boot烧写镜像到SD卡的过程(下篇:Makefile文件)

上文讲到,如果需求仅略微修改,整个从编译到仅保留二进制文件到添加HeaderInfo到烧写到SD卡的一系列命令都需要重新再输入一遍,这很繁琐。

如何解决这个问题呢?

制作一个bash脚本文件

制作一个bash脚本文件,也就是制作一个批处理文件

#!/bin/bash

arm-linux-gcc -c mystart.s
arm-linux-gcc -c mylowlevel_init.s			//.s文件生成.o文件

arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o	//.o文件生成可执行文件

arm-linux-objcopy -O binary myboot myboot.bin			//只保留二进制文件

./mkv210 u-boot.bin u-boot.16k					//添加HeaderInfo

sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1		//烧写到SD卡

然后运行这个文件:

bash genmyboot.sh

虽然,这样也能够实现功能,但是某种程度上可以看出并不智能。什么意思呢?可能某次修改,只修改了其中的某几个文件,其他的很多文件都没有修改。但如果运行这个bash,所有的程序都要走一遍,也就是无论文件是否被修改,都会被处理,会浪费很多的时间。

Makefile

无需畏惧Makefile

Makefile是一个能够让初学者很挫败的文件,初看会让人头昏眼花,感觉在看“天书”。比如下面是U-BootMakefile中很少的一部分:

$(obj)u-boot.img:	$(obj)u-boot.bin
		$(obj)tools/mkimage -A $(ARCH) -T firmware -C none \
		-O u-boot -a $(CONFIG_SYS_TEXT_BASE) -e 0 \
		-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
			sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
		-d $< $@

$(obj)u-boot.ubl:       $(obj)spl/u-boot-spl.bin $(obj)u-boot.bin
		$(OBJCOPY) ${OBJCFLAGS} --pad-to=$(PAD_TO) -O binary $(obj)spl/u-boot-spl $(obj)spl/u-boot-spl-pad.bin
		cat $(obj)spl/u-boot-spl-pad.bin $(obj)u-boot.bin > $(obj)u-boot-ubl.bin
		$(obj)tools/mkimage -n $(UBL_CONFIG) -T ublimage \
		-e $(CONFIG_SYS_TEXT_BASE) -d $(obj)u-boot-ubl.bin $(obj)u-boot.ubl
		rm $(obj)u-boot-ubl.bin
		rm $(obj)spl/u-boot-spl-pad.bin

$(obj)u-boot.ais:       $(obj)spl/u-boot-spl.bin $(obj)u-boot.img
		$(obj)tools/mkimage -s -n $(if $(CONFIG_AIS_CONFIG_FILE),$(CONFIG_AIS_CONFIG_FILE),"/dev/null") \
			-T aisimage \
			-e $(CONFIG_SPL_TEXT_BASE) \
			-d $(obj)spl/u-boot-spl.bin \
			$(obj)spl/u-boot-spl.ais
		$(OBJCOPY) ${OBJCFLAGS} -I binary \
			--pad-to=$(CONFIG_SPL_MAX_SIZE) -O binary \
			$(obj)spl/u-boot-spl.ais $(obj)spl/u-boot-spl-pad.ais
		cat $(obj)spl/u-boot-spl-pad.ais $(obj)u-boot.img > \
			$(obj)u-boot.ais

是不是很费解?Makefile其实语法比较简单,但复杂就复杂在其中运用了许多shell脚本和各种正则表达式等,这些混杂在一起,就会让人摸不着头脑。

如果需要详细了解Makefile的内容,可以参考链接:跟我一起写Makefile

Makefile的基本语法

target ... : prerequisites ...
    command
    ...
    ...

具体含义为:

  • target:可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label);
  • prerequisites:生成该target所依赖的文件和/或target;
  • command:该target要执行的命令(任意的shell命令)。

这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件, 其生成规则定义在command。说白一点就是说:prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行

这就是Makefile的规则,也就是Makefile中最核心的内容。

Makefile案例

既然大致了解了Makefile的规则,那么如何才能完成制作描述U-Boot烧写镜像到SD卡的过程的Makefile呢?

.s文件生成.o文件

先根据这个简单的规则,写一个简单的Makefile。这里首先注明一下make命令的寻找优先级,make会在当前目录下寻找以下的Makefile文件,优先级由高到低为:

GUNMakefile > makefile > Makefile

一般,文件命名为Makefile

制作一个Makefile文件,用于将mystart.s汇编成mystart.o文件:

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s

这很简单,如果同时还需要将mylowlevel_init.s汇编成mylowlevel_init.o文件:

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s
	
mylowlevel_init.o: mylowlevel_init.s
	arm-linux-gcc -c mylowlevel_init.s

如果此时,还是运行Makefile,会发现只运行了前一句指令,但后一句的指令并没有被运行。这是因为:Makefile只运行第一个目标target,其余的目标target都不会被运行

怎么解决呢?利用Makefile生成一个伪目标target,再利用伪目标target去生成接下来的两个目标target。方式如下:

.PHONY:all				//注明all为伪目标(可写可不写)
all: mystart.o mylowlevel_init.o

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s
	
mylowlevel_init.o: mylowlevel_init.s
	arm-linux-gcc -c mylowlevel_init.s

make的时候,目标all本身不运行任何命令,但是它依赖于mystart.omylowlevel_init.o。但当前目录下并没有这两个文件,因此它就会自动在当前Makefile中寻找是否存在目标target,如果存在,就自动执行该目标的命令。

其余步骤

.PHONY:all				//注明all为伪目标(可写可不写)
all: mystart.o mylowlevel_init.o
	arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o
	arm-linux-objcopy -O binary myboot myboot.bin
	./mkv210 u-boot.bin u-boot.16k

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s
	
mylowlevel_init.o: mylowlevel_init.s
	arm-linux-gcc -c mylowlevel_init.s

由于烧写到SD卡的命令,需要SD卡已经插入的状态,一般而言,不会将它和这些命令都写在一起。但是,每次都输入这么麻烦的代码,也是很麻烦的事情。于是,可以用另一种方式:

.PHONY:all				//注明all为伪目标(可写可不写)
all: mystart.o mylowlevel_init.o
	arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o
	arm-linux-objcopy -O binary myboot myboot.bin
	./mkv210 u-boot.bin u-boot.16k

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s
	
mylowlevel_init.o: mylowlevel_init.s
	arm-linux-gcc -c mylowlevel_init.s

.PHONY:mksd
mksd:
	sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1

创造一个伪目标,专门用于烧写镜像到SD卡中。但是,一般情况下,这句伪目标是运行不到的。那怎么样才能只运行这句伪目标呢?

只需要在make的时候,人为地指定伪目标即可:

make mksd

Makefile的改进

尽管此时Makefile的运行没有问题,但是还是会发现一个问题。如果修改了mylowlevel_init.s文件,只会将mylowlevel_init.s文件会变成mylowlevel_init.o文件,mystart.s并不会重新汇编,这很不错;但是如果两个都不修改,此时两个.s文件都不会会变成.o文件,但是后面链接、只保留二进制文件、添加HeaderInfo三句依然还是会运行!

这是为什么呢?

由于all是一个伪目标,没有办法进行目标与依赖之间的新旧关系,因此就会一直都会运行后面的三句。

改进后的代码为:

.PHONY:all				//注明all为伪目标(可写可不写)
all: myboot

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s
	
mylowlevel_init.o: mylowlevel_init.s
	arm-linux-gcc -c mylowlevel_init.s

myboot: mystart.o mylowlevel_init.o myboot.lds
	arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o
	arm-linux-objcopy -O binary myboot myboot.bin
	./mkv210 u-boot.bin u-boot.16k
	
.PHONY:mksd
mksd:
	sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1

如此便好,也就是说,最好让伪目标没有命令可以执行

Makefile的自动化变量

尽管上文的Makefile看起来比较“优雅”了,但是还是存在问题的:如果存在100个.s文件需要汇编成.o文件,那么需要写100条类似于如下的代码。

mystart.o: mystart.s
	arm-linux-gcc -c mystart.s

这想一想,也是非常繁琐。于是,Makefile就引进了:

  • 自动化变量:$@(所有目标target集合)、$^(所有依赖集合)、$<(所有依赖集合中的第一个)
  • 模式匹配:%.x(当前目录下所有.x结尾的文件)

有了自动化变量和模式匹配,就可以写出更加简洁的Makefile了:

.PHONY:all				//注明all为伪目标(可写可不写)
all: myboot

%.o: %.s
	arm-linux-gcc -c $<

myboot: mystart.o mylowlevel_init.o myboot.lds
	arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o
	arm-linux-objcopy -O binary myboot myboot.bin
	./mkv210 u-boot.bin u-boot.16k
	
.PHONY:mksd
mksd:
	sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1

同样,可以使用变量来代替某些内容,有点类似于宏定义的样子。一般采用:=来赋值,引用的时候需要用$()来引用

CC := arm-linux-gcc

.PHONY:all				//注明all为伪目标(可写可不写)
all: myboot

%.o: %.s
	$(CC) -c $<

myboot: mystart.o mylowlevel_init.o myboot.lds
	arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o
	arm-linux-objcopy -O binary myboot myboot.bin
	./mkv210 u-boot.bin u-boot.16k
	
.PHONY:mksd
mksd:
	sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1

当然,U-BootMakefile肯定比本文的要复杂得多的多,之后的博文会对此进行详细分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值