现在的位置: 首页 > 综合 > 正文

RTEMS应用程序的目录管理:Makefile写法

2013年09月20日 ⁄ 综合 ⁄ 共 11295字 ⁄ 字号 评论关闭

最近有个朋友,在网上问我一个编译问题。我和他讨论了一下,大致确定问题的位置了。即应用程序的目录问题。
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

抱歉!评论已关闭.