RTEMS一般简单的应用程序都是采用平面目录结构:
RTEMS_APP_DIR
|--main.c
|--main.h
|--init.c
|--other.c
`--Makefile
即所有的.c、.h、.cpp文件都在RTEMS_APP_DIR下。Makefile写好以后,编译成功后直接生成一个o-optimize的目录,这个目录下有这个应用程序的名称。这个问题看似简单,实际上很复杂。以
network-demos-4.9.5\telnetd为例,它的
文件结构是:
telnetd
|--init.c
`--Makefile
这种简单的工程,这种平面式的工程管理,自然是没有任何问题的。但一旦工程复杂起来,这样管理谁都不能接受。然而加个目录的问题就不像我们想象的那么简单。我们进行如下的修改:
telnetd
|--main
| |--init.c
| `--test
| `--test2.c
|--test.c
`--Makefile
test.c的文件内容如下:
void test(void)
{
}
test2.c的文件内容如下:
void test2(void)
{
extern void test(void);
test();
}
init.c里的相关内容也要修改一下(不全列出来了):
第38行的
#include "../networkconfig.h"
改为:
#include "../../networkconfig.h"
理由就不说了,你懂得。
在151行和152行之间:
151: {
152: fprintf(stderr, "\n\n*** Telnetd Server Test ***\n\r" );
修改为:
151: {
152: extern void test2();
153: test2();
154: fprintf(stderr, "\n\n*** Telnetd Server Test ***\n\r" );
理由略,你懂得。
我们把Makefile
1: #
2: # $Id: Makefile,v 1.1.2.1 2009/05/18 17:50:53 joel Exp $
3: #
4:
5: SAMPLE=telnetd
6: PGM=${ARCH}/$(SAMPLE).exe
7:
8: MANAGERS=all
9:
10: # C source names, if any, go here -- minus the .c
11: C_FILES=init.c
12: C_O_FILES=$(C_FILES:%.c=${ARCH}/%.o)
13:
14: H_FILES=
中的第11行修改为:
C_FILES=main/test/test2.c main/init.c test.c
代码是没有什么问题,我们输入make编译。
[root@localhost telnetd]# make
test -d o-optimize || mkdir o-optimize
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -c
-o o-optimize/main/test/test2.o main/test/test2.c
Assembler messages:
Fatal error: can't create o-optimize/main/test/test2.o: No such file or directory
make: *** [o-optimize/main/test/test2.o] Error 2
好了,我们得到一个错误。从错误上分析,o-optimize/mian/test/test2.o没能正确生成。从上一条gcc命令上看,-o o-optimize/main/test/test2.o main/test/test2.c,可能是o-optimize目录下没有main和test目录,造成生成失败。我的目标是什么?所有生成的目标文件以平面形式存储在o-optimize下,并能成功编译这个工程。看来这个问题不是个好解决的问题啊。既然o-optimize下没有main等目录,但是根目录下有啊。所以把Makefile中的11和12行修改为:
C_FILES=main/test/test2.c main/init.c test.c
C_O_FILES=$(C_FILES:%.c=${ARCH}/../%.o)
make后,果然成功了。但是目录结构是:
telnetd
|-- Makefile
|-- main
| |-- init.c
| |-- init.o
| `-- test
| |-- test2.c
| `-- test2.o
|-- o-optimize
| |-- telnetd.exe
| |-- telnetd.num
| `-- telnetd.ralf
|-- test.c
`-- test.o
从目录上看,每个生成的*.o文件都在各自的目录夹里。虽然能编译成功,但这不是我们想要的结果。因为,当我们输入make clean命令后:
telnetd
|-- Makefile
|-- main
| |-- init.c
| |-- init.o
| `-- test
| |-- test2.c
| `-- test2.o
`-- test.c
可以很明显看到,在子目录下的.o文件全部都不能清除,看来这不是我们要的结果。
我尝试这样修改Makefile中的第11行和第12行:
C_FILES=main/test/test2.c main/init.c test.c
C_O_FILES=$(C_FILES:../%.c=${ARCH}/%.o)
输入make命令:
[root@localhost telnetd]# make
test -d o-optimize || mkdir o-optimize
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -
mcpu=arm920t -mstructure-size-boundary=32 -o o-optimize/telnetd.exe main/test/test2.c main/init.c test.c -ltelnetd
arm-rtems4.9-nm -g -n o-optimize/telnetd.exe > o-optimize/telnetd.num
arm-rtems4.9-size o-optimize/telnetd.exe
text data bss dec hex filename
691544 12892 196684 901120 dc000 o-optimize/telnetd.exe
cp o-optimize/telnetd.exe o-optimize/telnetd.ralf
可以看到,编译成功了。总觉得怪怪的,编译后的目录结构是:
telnetd
|-- Makefile
|-- main
| |-- init.c
| `-- test
| `-- test2.c
|-- o-optimize
| |-- telnetd.exe
| |-- telnetd.num
| `-- telnetd.ralf
`-- test.c
奇怪,我的.o文件呢?怎么都不见了?仔细看make命令的输出,怎么没有test.c init.c等文件的单个编译输出呢?在Makefile文件的第12行后添加一句:
$(warning $(C_O_FILES))
再输入make命令,得到:
[root@localhost telnetd]# make
Makefile:13: main/test/test2.c main/init.c test.c
test -d o-optimize || mkdir o-optimize
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -
mcpu=arm920t -mstructure-size-boundary=32 -o o-optimize/telnetd.exe main/test/test2.c main/init.c test.c -ltelnetd
arm-rtems4.9-nm -g -n o-optimize/telnetd.exe > o-optimize/telnetd.num
arm-rtems4.9-size o-optimize/telnetd.exe
text data bss dec hex filename
691544 12892 196684 901120 dc000 o-optimize/telnetd.exe
cp o-optimize/telnetd.exe o-optimize/telnetd.ralf
看到第13行的输出:
Makefile:13: main/test/test2.c main/init.c test.c,我汗啊,这样一改,生成的OBJ文件的后缀名不是.o,而是.c,系统没有覆盖源代码,在内存中完成的连接。看来这个问题这样也是解决不掉的。
痛定思痛,如果obj文件全部在o-optimize目录下。那么,由源文件生成目标文件时就要变化一下,第11、12行改为:
C_FILES=main/test/test2.c main/init.c test.c
C_O_FILES=$(addprefix ${ARCH}/, $(notdir $(C_FILES:%.c=${ARCH}/%.o)))
输入make命令得到这样的结果:
[root@localhost telnetd]# make
make: *** No rule to make target `o-optimize/test2.o', needed by `all'. Stop.
没有test2.o的规则?看Makefile的58行:
57:
58: ${ARCH}/init.o: ${ARCH} init.c
59:
只定义了init.c的规则,那么我们把余下的规则添加上
58: ${ARCH}/test.o: ${ARCH} test.c
59:
60: ${ARCH}/init.o: main/init.c
61:
62: ${ARCH}/test2.o: main/test/test2.c
63:
输入make命令:
[root@localhost telnetd]# make
test -d o-optimize || mkdir o-optimize
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -c
-o o-optimize/test.o test.c
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -
mcpu=arm920t -mstructure-size-boundary=32 -o o-optimize/telnetd.exe o-optimize/test2.o o-optimize/init.o o-optimize/test.o -ltelnetd
arm-rtems4.9-gcc: o-optimize/test2.o: No such file or directory
arm-rtems4.9-gcc: o-optimize/init.o: No such file or directory
make: *** [o-optimize/telnetd.exe] Error 1
晕死,竟然得到了两个错误,竟然没有test2和init.o的规则!然而我们明明有规则的。为什么会这样呢?除非有预先定义的规则。这个Makefile我翻来覆去的看了一下,明白了。第24、25、26行有:
include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
include $(RTEMS_CUSTOM)
include $(PROJECT_ROOT)/make/leaf.cfg
于是查看leaf.cfg,在/opt/rtems-4.9/make/下,leaf.cfg文件第58行是
include ${CONFIG.CC}
CONFIG.CC在哪里定义呢?
/opt/rtems-4.9/make/custom/default.cfg中定义了
CONFIG.CC = $(RTEMS_ROOT)/make/compilers/gcc-target-default.cfg
我们来看看/opt/rtems-4.9/make/compilers/gcc-target-default.cfg中的部分内容(第103行到129行):
${ARCH}/%.o: %.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
${ARCH}/%.o: %.cc
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
${ARCH}/%.o: %.cpp
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
${ARCH}/%.o: %.cxx
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
${ARCH}/%.o: %.C
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
${ARCH}/%.o: %.S
${COMPILE.S} $(AM_CPPFLAGS) -DASM -o $@ $<
# Make foo.rel from foo.o
${ARCH}/%.rel: ${ARCH}/%.o
${make-rel}
# create $(ARCH)/pgm from pgm.sh
${ARCH}/%: %.sh
$(RM) $@
$(CP) $< $@
$(CHMOD) +x $@
看来,是这里设置了规则。我们可以把这个规则重载了,在telnetd/Makefile的第58行更改为:
${ARCH}/test.o:test.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
${ARCH}/init.o: main/init.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
${ARCH}/test2.o: main/test/test2.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
输入make命令:
[root@localhost telnetd]# make
test -d o-optimize || mkdir o-optimize
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -c
-o o-optimize/test2.o main/test/test2.c
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -c
-o o-optimize/init.o main/init.c
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -c
-o o-optimize/test.o test.c
arm-rtems4.9-gcc --pipe -B/opt/rtems-4.9/arm-rtems4.9/mini2440/lib/ -specs bsp_specs -qrtems -g -Wall -O0 -g -g -mcpu=arm920t -mstructure-size-boundary=32 -
mcpu=arm920t -mstructure-size-boundary=32 -o o-optimize/telnetd.exe o-optimize/test2.o o-optimize/init.o o-optimize/test.o -ltelnetd
arm-rtems4.9-nm -g -n o-optimize/telnetd.exe > o-optimize/telnetd.num
arm-rtems4.9-size o-optimize/telnetd.exe
text data bss dec hex filename
691544 12892 196684 901120 dc000 o-optimize/telnetd.exe
cp o-optimize/telnetd.exe o-optimize/telnetd.ralf
可以看到编译成功了。目录结构为:
telnetd
|-- Makefile
|-- main
| |-- init.c
| `-- test
| `-- test2.c
|-- o-optimize
| |-- init.o
| |-- telnetd.exe
| |-- telnetd.num
| |-- telnetd.ralf
| |-- test.o
| `-- test2.o
`-- test.c
经过这个过程,并没有什么成就感。因为如果工程文件中有大量的目录和文件,实际上这种手工的修改很难维护项目。要有一个自动化的Makefile方案才好。通过阅读Makefile手册可知,利用foreach语句和eval函数,可以完成这一过程。我就把我整个调试过程省略了。直接上代码了:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).c,${SRCS});${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $$@ $$<))
以下三句话,可以用以上一句话完成。
${ARCH}/test.o:test.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
${ARCH}/init.o: main/init.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
${ARCH}/test2.o: main/test/test2.c
${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $@ $<
由于使用foreach函数,无论有多少*.c文件,都会一一设置规则,省去手动维护的烦恼。注意,这里只是*.c文件的编译规则。如果工程中支持C++,还有汇编语言,那么
${ARCH}/%.o: %.cc
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
对应于:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).c,${SRCS});${COMPILE.c} $(AM_CPPFLAGS)
$(AM_CFLAGS) -o $$@ $$<))
${ARCH}/%.o: %.cpp
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
对应于:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).cpp,${CPPSRCS});${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $$@ $$<))
${ARCH}/%.o: %.cxx
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
对应于:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).cxx,${CPPSRCS});${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $$@ $$<))
${ARCH}/%.o: %.C
${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CXXFLAGS) -o $@ $<
对应于:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).C,${CPPSRCS});${COMPILE.cc} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $$@ $$<))
${ARCH}/%.o: %.S
${COMPILE.S} $(AM_CPPFLAGS) -DASM -o $@ $<
对应于:
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).S,${ASMSRCS});${COMPILE.S} $(AM_CPPFLAGS) -DASM -o $$@ $$<))
不过说实话,我调试了foreach和eval函数用了2、3个小时。主要是对foreach和eval以及Makefile的语法不熟悉。才弄了这么久,所以在结束本文前,推荐大家个Makefile的好网站:
《GNU make中文手册ver - 3.8》
http://www.linuxsir.org/main/doc/gnumake/GNUmake_v3.80-zh_CN_html/index.html#content
很好用的网站。
最最后,附上Makefile的全部内容:
#
# $Id: Makefile,v 1.1.2.1 2009/05/18 17:50:53 joel Exp $
#
SAMPLE=telnetd
PGM=${ARCH}/$(SAMPLE).exe
MANAGERS=all
# C source names, if any, go here -- minus the .c
C_FILES=main/test/test2.c main/init.c test.c
C_O_FILES=$(addprefix ${ARCH}/, $(notdir $(C_FILES:%.c=${ARCH}/%.o)))
H_FILES=
DOCTYPES=
DOCS=$(DOCTYPES:%=$(SAMPLE).%)
SRCS=$(DOCS) $(C_FILES)
OBJS=$(C_O_FILES)
PRINT_SRCS=$(DOCS)
include $(RTEMS_MAKEFILE_PATH)/Makefile.inc
include $(RTEMS_CUSTOM)
#overide CONFIG.CC=gcc-target-my.cfg
include $(PROJECT_ROOT)/make/leaf.cfg
#
# (OPTIONAL) Add local stuff here using +=
#
override DEFINES +=
CPPFLAGS +=
CFLAGS_LD +=
CFLAGS_OPTIMIZE_V +=
CFLAGS_DEBUG_V += -v -qrtems_debug
LD_PATHS +=
override LD_LIBS += -ltelnetd
CFLAGS +=
#
# Add your list of files to delete here. The config files
# already know how to delete some stuff, so you may want
# to just run 'make clean' first to see what gets missed.
# 'make clobber' already includes 'make clean'
#
CLEAN_ADDITIONS +=
CLOBBER_ADDITIONS +=
all: ${ARCH} $(SRCS) $(PGM) $(OBJS)
$(foreach each_obj_files, $(OBJS), $(eval $${ARCH}/$(notdir $(each_obj_files)):$(filter %$(basename $(notdir $(each_obj_files))).c,${SRCS});${COMPILE.c} $(AM_CPPFLAGS) $(AM_CFLAGS) -o $$@ $$<))
${PGM}: ${ARCH} $(OBJS) $(LINK_FILES)
$(make-exe)
# Install the program(s), appending _g or _p as appropriate.
# for include files, just use $(INSTALL)
install: all
$(INSTALL_VARIANT) -m 555 ${PGM} ${PROJECT_RELEASE}/tests