【Linux】制作U-Boot烧写镜像到SD卡的过程(下篇:Makefile文件)
上文講到,如果需求僅略微修改,整個(gè)從編譯到僅保留二進(jìn)制文件到添加HeaderInfo到燒寫到SD卡的一系列命令都需要重新再輸入一遍,這很繁瑣。
如何解決這個(gè)問題呢?
制作一個(gè)bash腳本文件
制作一個(gè)bash腳本文件,也就是制作一個(gè)批處理文件:
#!/bin/basharm-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文件生成可執(zhí)行文件arm-linux-objcopy -O binary myboot myboot.bin //只保留二進(jìn)制文件./mkv210 u-boot.bin u-boot.16k //添加HeaderInfosudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1 //燒寫到SD卡然后運(yùn)行這個(gè)文件:
bash genmyboot.sh雖然,這樣也能夠?qū)崿F(xiàn)功能,但是某種程度上可以看出并不智能。什么意思呢?可能某次修改,只修改了其中的某幾個(gè)文件,其他的很多文件都沒有修改。但如果運(yùn)行這個(gè)bash,所有的程序都要走一遍,也就是無論文件是否被修改,都會(huì)被處理,會(huì)浪費(fèi)很多的時(shí)間。
Makefile
無需畏懼Makefile
Makefile是一個(gè)能夠讓初學(xué)者很挫敗的文件,初看會(huì)讓人頭昏眼花,感覺在看“天書”。比如下面是U-Boot的Makefile中很少的一部分:
$(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.bincat $(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.ublrm $(obj)u-boot-ubl.binrm $(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.aiscat $(obj)spl/u-boot-spl-pad.ais $(obj)u-boot.img > \$(obj)u-boot.ais是不是很費(fèi)解?Makefile其實(shí)語法比較簡單,但復(fù)雜就復(fù)雜在其中運(yùn)用了許多shell腳本和各種正則表達(dá)式等,這些混雜在一起,就會(huì)讓人摸不著頭腦。
如果需要詳細(xì)了解Makefile的內(nèi)容,可以參考鏈接:跟我一起寫Makefile。
Makefile的基本語法
target ... : prerequisites ...command......具體含義為:
- target:可以是一個(gè)object file(目標(biāo)文件),也可以是一個(gè)執(zhí)行文件,還可以是一個(gè)標(biāo)簽(label);
- prerequisites:生成該target所依賴的文件和/或target;
- command:該target要執(zhí)行的命令(任意的shell命令)。
這是一個(gè)文件的依賴關(guān)系,也就是說,target這一個(gè)或多個(gè)的目標(biāo)文件依賴于prerequisites中的文件, 其生成規(guī)則定義在command中。說白一點(diǎn)就是說:prerequisites中如果有一個(gè)以上的文件比target文件要新的話,command所定義的命令就會(huì)被執(zhí)行。
這就是Makefile的規(guī)則,也就是Makefile中最核心的內(nèi)容。
Makefile案例
既然大致了解了Makefile的規(guī)則,那么如何才能完成制作描述U-Boot燒寫鏡像到SD卡的過程的Makefile呢?
.s文件生成.o文件
先根據(jù)這個(gè)簡單的規(guī)則,寫一個(gè)簡單的Makefile。這里首先注明一下make命令的尋找優(yōu)先級(jí),make會(huì)在當(dāng)前目錄下尋找以下的Makefile文件,優(yōu)先級(jí)由高到低為:
GUNMakefile > makefile > Makefile
一般,文件命名為Makefile。
制作一個(gè)Makefile文件,用于將mystart.s匯編成mystart.o文件:
mystart.o: mystart.sarm-linux-gcc -c mystart.s這很簡單,如果同時(shí)還需要將mylowlevel_init.s匯編成mylowlevel_init.o文件:
mystart.o: mystart.sarm-linux-gcc -c mystart.smylowlevel_init.o: mylowlevel_init.sarm-linux-gcc -c mylowlevel_init.s如果此時(shí),還是運(yùn)行Makefile,會(huì)發(fā)現(xiàn)只運(yùn)行了前一句指令,但后一句的指令并沒有被運(yùn)行。這是因?yàn)?#xff1a;Makefile只運(yùn)行第一個(gè)目標(biāo)target,其余的目標(biāo)target都不會(huì)被運(yùn)行。
怎么解決呢?利用Makefile生成一個(gè)偽目標(biāo)target,再利用偽目標(biāo)target去生成接下來的兩個(gè)目標(biāo)target。方式如下:
.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: mystart.o mylowlevel_init.omystart.o: mystart.sarm-linux-gcc -c mystart.smylowlevel_init.o: mylowlevel_init.sarm-linux-gcc -c mylowlevel_init.s當(dāng)make的時(shí)候,目標(biāo)all本身不運(yùn)行任何命令,但是它依賴于mystart.o和mylowlevel_init.o。但當(dāng)前目錄下并沒有這兩個(gè)文件,因此它就會(huì)自動(dòng)在當(dāng)前Makefile中尋找是否存在目標(biāo)target,如果存在,就自動(dòng)執(zhí)行該目標(biāo)的命令。
其余步驟
.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: mystart.o mylowlevel_init.oarm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.oarm-linux-objcopy -O binary myboot myboot.bin./mkv210 u-boot.bin u-boot.16kmystart.o: mystart.sarm-linux-gcc -c mystart.smylowlevel_init.o: mylowlevel_init.sarm-linux-gcc -c mylowlevel_init.s由于燒寫到SD卡的命令,需要SD卡已經(jīng)插入的狀態(tài),一般而言,不會(huì)將它和這些命令都寫在一起。但是,每次都輸入這么麻煩的代碼,也是很麻煩的事情。于是,可以用另一種方式:
.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: mystart.o mylowlevel_init.oarm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.oarm-linux-objcopy -O binary myboot myboot.bin./mkv210 u-boot.bin u-boot.16kmystart.o: mystart.sarm-linux-gcc -c mystart.smylowlevel_init.o: mylowlevel_init.sarm-linux-gcc -c mylowlevel_init.s.PHONY:mksd mksd:sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1創(chuàng)造一個(gè)偽目標(biāo),專門用于燒寫鏡像到SD卡中。但是,一般情況下,這句偽目標(biāo)是運(yùn)行不到的。那怎么樣才能只運(yùn)行這句偽目標(biāo)呢?
只需要在make的時(shí)候,人為地指定偽目標(biāo)即可:
make mksdMakefile的改進(jìn)
盡管此時(shí)Makefile的運(yùn)行沒有問題,但是還是會(huì)發(fā)現(xiàn)一個(gè)問題。如果修改了mylowlevel_init.s文件,只會(huì)將mylowlevel_init.s文件會(huì)變成mylowlevel_init.o文件,mystart.s并不會(huì)重新匯編,這很不錯(cuò);但是如果兩個(gè)都不修改,此時(shí)兩個(gè).s文件都不會(huì)會(huì)變成.o文件,但是后面鏈接、只保留二進(jìn)制文件、添加HeaderInfo三句依然還是會(huì)運(yùn)行!
這是為什么呢?
由于all是一個(gè)偽目標(biāo),沒有辦法進(jìn)行目標(biāo)與依賴之間的新舊關(guān)系,因此就會(huì)一直都會(huì)運(yùn)行后面的三句。
改進(jìn)后的代碼為:
.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: mybootmystart.o: mystart.sarm-linux-gcc -c mystart.smylowlevel_init.o: mylowlevel_init.sarm-linux-gcc -c mylowlevel_init.smyboot: mystart.o mylowlevel_init.o myboot.ldsarm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.oarm-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如此便好,也就是說,最好讓偽目標(biāo)沒有命令可以執(zhí)行。
Makefile的自動(dòng)化變量
盡管上文的Makefile看起來比較“優(yōu)雅”了,但是還是存在問題的:如果存在100個(gè).s文件需要匯編成.o文件,那么需要寫100條類似于如下的代碼。
mystart.o: mystart.sarm-linux-gcc -c mystart.s這想一想,也是非常繁瑣。于是,Makefile就引進(jìn)了:
- 自動(dòng)化變量:$@(所有目標(biāo)target集合)、$^(所有依賴集合)、$<(所有依賴集合中的第一個(gè))
- 模式匹配:%.x(當(dāng)前目錄下所有.x結(jié)尾的文件)
有了自動(dòng)化變量和模式匹配,就可以寫出更加簡潔的Makefile了:
.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: myboot%.o: %.sarm-linux-gcc -c $<myboot: mystart.o mylowlevel_init.o myboot.ldsarm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.oarm-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同樣,可以使用變量來代替某些內(nèi)容,有點(diǎn)類似于宏定義的樣子。一般采用:=來賦值,引用的時(shí)候需要用$()來引用:
CC := arm-linux-gcc.PHONY:all //注明all為偽目標(biāo)(可寫可不寫) all: myboot%.o: %.s$(CC) -c $<myboot: mystart.o mylowlevel_init.o myboot.ldsarm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.oarm-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當(dāng)然,U-Boot的Makefile肯定比本文的要復(fù)雜得多的多,之后的博文會(huì)對(duì)此進(jìn)行詳細(xì)分析。
總結(jié)
以上是生活随笔為你收集整理的【Linux】制作U-Boot烧写镜像到SD卡的过程(下篇:Makefile文件)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 半年以来的图像去雾总结-图像去雾(一)暗
- 下一篇: 【Linux】制作U-Boot烧写镜像到