繼續上篇未完的內容。
%.hex: %.ihx
$(PACKIHX) $< > $@
這裏我們尋找依賴文件hello-world.ihx的建立規則,
%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib
$(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null
繼續尋找obj_cc2530/hello-world.app.rel 的建立規則
$(OBJECTDIR)/%.app.rel: %.c $(SEGMENT_RULES)
$(CC) $(call c_seg,$<,$@) -DAUTOSTART_ENABLE $(CFLAGS) -c $< -o $@
依賴文件hello-world.c文件已經存在,所以尋找SEGMENT_RULES變量所代替的文件的重建規則。
SEGMENT_RULE_FILES = $(foreach dir, . $(CONTIKI_PLATFORM_DIRS) \
$(CONTIKI_CPU_DIRS_LIST), $(wildcard $(dir)/segment.rules) )
$(SEGMENT_RULES): $(SEGMENT_RULE_FILES)
cat $(SEGMENT_RULE_FILES) | \
sed -e 's/#.*$$//' -e 's/^\s*//' -e '/^$$/d' > $@
經過分析我們知道SEGMENT_RULE_FILES表示的在某些目錄中的segment.rules文件列表
好!我們找到了這個文件,分別是cpu/cc253x/segment.rules platform/cc2530dk/segment.rules
既然找到了,而且肯定比目標新,那麼就執行命令。這裏用到了管道,將這些segment.ruls的內容最爲sed的輸入調用sed命令,讓後將其重定向到目標文件obj_cc2530dk/segment.rules中,這個文件不存在,那麼就創建它。
這個sed命令有點煩,因爲它用到了正則表達式,-e選項表示後面跟的是正則表達式。s爲替換命令,我大致說一下意思,就是將segment.rules文件中不符合它要求的字符行全部替換爲空,或者刪除。這些不符合要求的行爲
1、以#開頭的第二個字符非換行符的任意行
2、以任意空白符開頭,後面接任意字符(或者沒有)的行
3、這裏知道^爲匹配行首,兩個$表示一個$符號,表示匹配行尾,那麼就是空白行
其實我們可以去segment.rules文件裏面去看看,一看就明白了,我把最終定向生成的文件給大家看看
看到了吧 就只剩下幾個segment的語句了。這幾句表示什麼意思,我們可以不用管!這是編譯器的事!
$(OBJECTDIR)/%.app.rel: %.c $(SEGMENT_RULES)
$(CC) $(call c_seg,$<,$@) -DAUTOSTART_ENABLE $(CFLAGS) -c $< -o $@
既然生成了$(SEGMENT_RULES)即obj_cc2530dk/segment.rules,hello-world.c文件也存在,那麼執行下面這個命令,這裏才真正用到了sdcc編譯器的編譯命令,調用了c_seg函數,這裏我找到了它的定義
ifeq ($(HAVE_BANKING),1)
## Yes
MEMORY_MODEL=huge
LDFLAGS += -Wl-r
LD_PRE_FLAGS += -Wl-bBANK1=0x018000
CFLAGS += -DHAVE_SDCC_BANKING
#use this in $(call c_seg,$<) to get segment for a source file.
c_seg = --codeseg $(shell python $(BANK_ALLOC) $1 $(SEGMENT_RULES) $2)
else
## No banking
MEMORY_MODEL=large
c_seg =
endif
顯然這裏c_seg爲空,因爲HAVE_BANKING定義爲0
所以sdcc的第一個參數爲空,-DAUTOSTART_ENABLE,定義宏DAUTOSTART_ENABLE,然後編譯.app.rel後綴的文件。這裏相當於生成裏hello-world.app.rel文件。好,我們繼續回溯,
%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib
$(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null
第一個依賴文件生成了,第二個依賴文件是obj_cc2530dk/contiki-main.rel(以.rel結尾的文件都是sdcc編譯產生的目標文件,相當c編譯器的.o文件),這個文件有嗎?貌似現在還沒有吧
那麼我們得找這個文件的生成規則,找到了一個模式規則;
$(OBJECTDIR)/%.rel: %.c $(SEGMENT_RULES)
$(CC) $(call c_seg,$<,$@) $(CFLAGS) -c $< -o $@ -Wp,-MMD,$(@:.rel=.d),-MQ,$@
@$(FINALIZE_SDCC_DEPENDENCY)
它的依賴文件爲contiki-main.c文件,我們在platform/cc2530dk目錄下找到了這個文件,那麼繼續找第二個依賴文件,顯然在上面已經生成了,所以調用下面的命令,這裏的sdcc命令添加了幾個預處理的選項-Wp,-MMD,$(@:.rel=.d),-MQ,$@這幾個什麼意思我們可以不用管它!
接着執行下面命令這是一個多行定義的變量
define FINALIZE_SDCC_DEPENDENCY
cp $(@:.rel=.d) $(@:.rel=.$$$$); \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(@:.rel=.$$$$) >> $(@:.rel=.d); \
rm -f $(@:.rel=.$$$$)
endef
我大致說下它的意思先將目標文件名替換成obj_cc2530dk/contiki-main.d,然後複製它,並以obj_cc2530dk/contiki-main.$$$$名字替換複製之後的文件名,然後以該目標文件爲輸入,重定向到sed命令中,然後再將sed的執行結果輸出到obj_cc2530dk/contiki-main.d文件中,最後刪除obj_cc2530dk/contiki-main.$$$$
好,我們現在生成了obj_cc2530dk/contiki-main.rel文件,回到這裏:
%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib
$(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki-$(TARGET).lib > /dev/null
看一下第三個依賴文件,繼續在makefile中找相關的模式匹配
contiki-$(TARGET).lib: $(CONTIKI_OBJECTFILES) $(PROJECT_OBJECTFILES) \
$(CONTIKI_ASMOBJECTFILES) $(CONTIKI_CASMOBJECTFILES)
rm -f $@
for target in $^; do echo $$target >> $@; done
它依賴文件顯然已經都生成了,因爲這些都是我們編譯生成的中間目標文件,所以直接執行下面的命令
開始強制刪除目標文件,爲什麼呢?因爲等哈我們要生成這個文件,所以先刪除舊的文件。
下面執行一個for循環,將規則中的所有依賴文件一次賦值給target,然後將其追加輸出到目標文件中。我們可以make整個工程之後查看contiki-cc2530dk.lib文件,裏面都是一些目標文件
現在生成了contiki-cc2530dk.lib文件,我們又回到了
%.ihx: $(OBJECTDIR)/%.app.rel $(CONTIKI_TARGET_MAIN) contiki-$(TARGET).lib
$(CC) $(LDFLAGS) -o $@ $(CONTIKI_TARGET_MAIN) $(OBJECTDIR)/$*.app.rel -llibsdcc.lib -lcontiki- $(TARGET).lib > /dev/null
貌似好幾次了。好,這三個依賴文件都生成了,那麼開始執行命令。這個命令沒得話說,命令的用法跟gcc差不多,將最終編譯輸出的信息定向到/dev/null中。
好,產生了hello-world.ihx文件,這個文件對有的cpu,它是可執行文件,但是對於我們cc2530來說還不是,所以還得將其轉換成.hex文件。回到
%.hex: %.ihx
$(PACKIHX) $< > $@
這個命令packihx將其轉換,我猜也只不過是一些頭信息的轉換。至此我們的hello-world.hex文件生成了,make工作結束了嗎?還沒有!回到這裏
%.$(TARGET): %.hex FORCE
cp $< $(<:.hex=.$(TARGET))
@echo "\nReport"
@echo "==============="
@echo 'Code footprint:'
@echo 'Area Addr Size' \
' Decimal'
@echo '---------------------------------- -------- --------' \
' --------'
@echo -n 'HOME,CSEG,CONST,XINIT,GS* $(HOME_START) '
@egrep ',CODE\)' $(<:.hex=.map) | egrep -v '(^BANK[1-9][^=])' | uniq | \
awk '{ SUM += $$5 } END { printf "%08X = %8d", SUM, SUM }'
@echo '. bytes (REL,CON,CODE)'
@egrep '(^BANK[1-9][^=])' $(<:.hex=.map) | uniq | sort
@egrep -A 5 'Other memory' $(<:.hex=.mem)
我們的目標是hello-world.cc2530dk,依賴文件已經生成了,那麼就執行下面命令,大概就是顯示一些編譯生成.hex文件中各個存儲區的大小,這裏我們不用關注。生成了hello-world.cc2530dk之後再回溯到Makefile.include文件中的
%: %.$(TARGET)
@
依賴文件生成了,那麼執行命令,這裏光一個@符號表示什麼呢,我們姑且當做是執行一個空命令,執行完之後生成helloworld文件,這是文件嗎,實際上着只是一個僞目標,是不存在的!那麼我們在目錄中當然就找不到這個文件了。然後回到最開始的Makefile文件中
all: $(CONTIKI_PROJECT)
對於另外blink-hello timer-test sensors-demo其make 的流程跟hello-world一樣的,也生成了幾個相應的hex文件。
最終構建終極目標,但是是僞目標,make並不產生這個文件。至此整個make的工作結束!
我們以cc2530dk的平臺爲例,將cc2530的工程編譯過程走了一遍,哪些地方需要添加我們自己定義的文件很一目瞭然了!那麼我們在寫自己的工程的時候,添加什麼文件,其文件名是什麼,這些都清楚了。
這五篇講解makefile的過程,我自己也學到不少make的知識,之前都是對它一知半解的,現在明朗多了!如果這中間有什麼問題,或分析的有錯,還請不吝指正!