继续上篇未完的内容。
%.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的知识,之前都是对它一知半解的,现在明朗多了!如果这中间有什么问题,或分析的有错,还请不吝指正!