【Linux】制作U-Boot烧写镜像到SD卡的过程(下篇:Makefile文件)
上文講到,如果需求僅略微修改,整個從編譯到僅保留二進制文件到添加HeaderInfo到燒寫到SD卡的一系列命令都需要重新再輸入一遍,這很繁瑣。
如何解決這個問題呢?
制作一個bash腳本文件
制作一個bash腳本文件,也就是制作一個批處理文件:
#!/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文件生成可執行文件arm-linux-objcopy -O binary myboot myboot.bin //只保留二進制文件./mkv210 u-boot.bin u-boot.16k //添加HeaderInfosudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1 //燒寫到SD卡然后運行這個文件:
bash genmyboot.sh雖然,這樣也能夠實現功能,但是某種程度上可以看出并不智能。什么意思呢?可能某次修改,只修改了其中的某幾個文件,其他的很多文件都沒有修改。但如果運行這個bash,所有的程序都要走一遍,也就是無論文件是否被修改,都會被處理,會浪費很多的時間。
Makefile
無需畏懼Makefile
Makefile是一個能夠讓初學者很挫敗的文件,初看會讓人頭昏眼花,感覺在看“天書”。比如下面是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是不是很費解?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.sarm-linux-gcc -c mystart.s這很簡單,如果同時還需要將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如果此時,還是運行Makefile,會發現只運行了前一句指令,但后一句的指令并沒有被運行。這是因為:Makefile只運行第一個目標target,其余的目標target都不會被運行。
怎么解決呢?利用Makefile生成一個偽目標target,再利用偽目標target去生成接下來的兩個目標target。方式如下:
.PHONY:all //注明all為偽目標(可寫可不寫) 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當make的時候,目標all本身不運行任何命令,但是它依賴于mystart.o和mylowlevel_init.o。但當前目錄下并沒有這兩個文件,因此它就會自動在當前Makefile中尋找是否存在目標target,如果存在,就自動執行該目標的命令。
其余步驟
.PHONY:all //注明all為偽目標(可寫可不寫) 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卡已經插入的狀態,一般而言,不會將它和這些命令都寫在一起。但是,每次都輸入這么麻煩的代碼,也是很麻煩的事情。于是,可以用另一種方式:
.PHONY:all //注明all為偽目標(可寫可不寫) 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創造一個偽目標,專門用于燒寫鏡像到SD卡中。但是,一般情況下,這句偽目標是運行不到的。那怎么樣才能只運行這句偽目標呢?
只需要在make的時候,人為地指定偽目標即可:
make mksdMakefile的改進
盡管此時Makefile的運行沒有問題,但是還是會發現一個問題。如果修改了mylowlevel_init.s文件,只會將mylowlevel_init.s文件會變成mylowlevel_init.o文件,mystart.s并不會重新匯編,這很不錯;但是如果兩個都不修改,此時兩個.s文件都不會會變成.o文件,但是后面鏈接、只保留二進制文件、添加HeaderInfo三句依然還是會運行!
這是為什么呢?
由于all是一個偽目標,沒有辦法進行目標與依賴之間的新舊關系,因此就會一直都會運行后面的三句。
改進后的代碼為:
.PHONY:all //注明all為偽目標(可寫可不寫) 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如此便好,也就是說,最好讓偽目標沒有命令可以執行。
Makefile的自動化變量
盡管上文的Makefile看起來比較“優雅”了,但是還是存在問題的:如果存在100個.s文件需要匯編成.o文件,那么需要寫100條類似于如下的代碼。
mystart.o: mystart.sarm-linux-gcc -c mystart.s這想一想,也是非常繁瑣。于是,Makefile就引進了:
- 自動化變量:$@(所有目標target集合)、$^(所有依賴集合)、$<(所有依賴集合中的第一個)
- 模式匹配:%.x(當前目錄下所有.x結尾的文件)
有了自動化變量和模式匹配,就可以寫出更加簡潔的Makefile了:
.PHONY:all //注明all為偽目標(可寫可不寫) 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同樣,可以使用變量來代替某些內容,有點類似于宏定義的樣子。一般采用:=來賦值,引用的時候需要用$()來引用:
CC := arm-linux-gcc.PHONY:all //注明all為偽目標(可寫可不寫) 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當然,U-Boot的Makefile肯定比本文的要復雜得多的多,之后的博文會對此進行詳細分析。
總結
以上是生活随笔為你收集整理的【Linux】制作U-Boot烧写镜像到SD卡的过程(下篇:Makefile文件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 半年以来的图像去雾总结-图像去雾(一)暗
- 下一篇: 【Linux】制作U-Boot烧写镜像到