跳至主要内容

【转】vivi开发笔记(三):Makefile详解

文章说明:calmarrow(lqm)原创

文章引自:http://piaoxiang.cublog.cn

 
    在Linux下面,要分析一个项目,首先应该读Makefile。前面有了一定的Makefile基础,现在虽然还不能写复杂的Makefile脚本,但是参考资料,还可以读懂vivi的Makefile。不过vivi的Makefile给我的感觉是在组织上有点凌乱,读起来有点费劲。只有真正理解了这个项目的组织,才能明白凌乱在何处。这中间都是按照自己的认识过程,尝试了各种方法来分析,基本上完成了按照自己理解改写的Makefile。编译完成重新下载到nand flash里面,验证没有问题。
 
    下面首先给出改写的Makefile。我尝试使用英语注释,可能不太通顺。但是,第一步总是要迈出的,早总比晚要好。
 

# Modified by Qingmin Liu(piaoxiangxinling@163.com, Shandong University)
# Date: 2007-07-26
# Desc: This document is only used for studying. If you want to talk about
#       vivi or embedded system, I am very glad to receive your email.

#
# Section 1: vivi version
#


# 3 rows below supplies with a numbering scheme, which comes from Linux
# kernel. This numbering scheme uses three numbers separated by dots to
# identify the releases. The first number designates the version, the
# second designates the patch, and the third designates the release.
#
# Usually, you should use a kernel from the latest stable series for your
# embedded system.
VERSION        = 0
PATCHLEVEL     = 1
SUBLEVEL       = 4

# This macro definations is used to supply users with version information,
# and you will find the usage in [init/version.c]. If you port vivi to your
# own board successfully, the version information displays at the beginning
# through the debug serial.
#
# VIVIRELEASE = 0.1.4
VIVIRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)

#
# Section 2: target architecture
#


# Architecture is arm. If you don't understand architecture, I think, you'd
# better search some data on the internet.
ARCH := arm

#
# Section 3: tools
#


# CONFIG_SHELL gets the shell that is used on your host. In my host, OS is
# Redhat Linux 9.0, and bash shell is the default one.
#
# This is used to execute the config scripts in [scripts]. And this is vivi's
# core about interactive configuration.
#

# You need to know some about Makefile if you don't know $(shell ..). In fact,
# it works to search the shell: /bin/bash --> sh. In my host, it works well.
# for example,
#    $which bash
#    /bin/bash
#    $which sh
#    /bin/sh
CONFIG_SHELL := $(shell if [ -x /bin/bash ]; then echo /bin/bash; \
                        else echo /bin/sh; fi)

# HOST tool
HOSTCC     = gcc
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
export HOSTCC HOSTCFLAGS

# TARGET tool
CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-

AS      = $(CROSS_COMPILE)as
LD      = $(CROSS_COMPILE)ld
CC      = $(CROSS_COMPILE)gcc
CPP     = $(CC) -E
AR      = $(CROSS_COMPILE)ar
NM      = $(CROSS_COMPILE)nm        
STRIP   = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump

export AS LD CC CPP AR

#
# Section 4: parameters

#


# TOPDIR means top directory of your project. More accurately, it means the
# directory of "Makefile" because you must step into the directory of Makefile
# if you want to execute make on your command line.
TOPDIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD;else /bin/pwd; fi)
export TOPDIR

# standard CFLAGS
CPPFLAGS  := -I$(TOPDIR)/include
CFLAGS    := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fPIC -fomit-frame-pointer
AFLAGS    := -D__ASSEMBLY__ $(CPPFLAGS)
export CFLAGS AFLAGS

# Location of the gcc arm libs.
ARM_GCC_LIBS = /usr/local/arm/2.95.3/lib/gcc-lib/arm-linux/2.95.3
OBJCOPYFLAGS = -R .comment -R .stab -R .stabstr

CLIBS     = -L$(ARM_GCC_LIBS) -lgcc -lc
LINKFLAGS = -Tarch/vivi.lds -Bstatic

#
# Section 5: files that will be included or handled with.
#


# whe you execute make on the command line, MAKEFILES are read at first.
MAKEFILES = $(TOPDIR)/.config

# core files and so on
CORE_FILES = init/main.o init/version.o lib/lib.o
LIBS       := lib/priv_data/priv_data.o
SUBDIRS    = drivers lib

# files that will be deleted when "make clean"
CLEAN_FILES =    \
        vivi-elf \
        vivi     \
        vivi.nm  \
        vivi.map

# files that will be deleted when "make distclean"
DISTCLEAN_FILES =                                      \
        include/autoconf.h .menuconfig.log             \
        scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
        .config .config.old TAGS tags

#

# Section 6: drivers added
#

# define CONFIGURATION if .config exists;
# else include .config
ifeq (.config, $(wildcard .config))
include .config
else
CONFIGURATION = config
endif

# drivers added by .config setup
# if CONFIG_SERIAL=y and CONFIG_MTD=y, DRIVERS equals "drivers/serial/serial.o drivers/mtd/mtd.o"
DRIVERS-y                   :=
DRIVERS-$(CONFIG_SERIAL)    += drivers/serial/serial.o
DRIVERS-$(CONFIG_MTD)       += drivers/mtd/mtd.o
DRIVERS                     := $(DRIVERS-y)

#
# Section 7: sub Makefile
#

include arch/Makefile

#

# Section 8: real handle
#

.PHONY: all do-it-all

# make all
# execute all handle procedure by the config
all: do-it-all

# search .config.If it exists, read .config first and equals "make Version; make vivi".
# else define CONFIGURATION and it equals "make config"
ifdef CONFIGURATION
do-it-all: $(CONFIGURATION)
else
do-it-all: Version vivi    
endif

# make Version
# delete [include/compile.h]
Version:
    $(RM) include/compile.h

# make vivi
# create target file -- vivi
# please remember: prerequisites in sequence.
vivi: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
    $(LD) -v $(LINKFLAGS)  \
        $(HEAD)            \
        $(CORE_FILES)      \
        $(DRIVERS)         \
        $(LIBS)            \
        -o $@-elf $(CLIBS)
    $(NM) -v -l $@-elf > $@.map
    $(OBJCOPY) -O binary -S $@-elf $@ $(OBJCOPYFLAGS)
    @echo
    @echo
" ^_^ The vivi boot image file is: $(shell pwd)/$@, and you \
            can download it to your [nor | nand] flash."

    @echo

# make oldconfig
# configure by the .config
oldconfig:
    $(CONFIG_SHELL) scripts/Configure -d arch/config.in

# make config
# configure by the [arch/defconfig]
config:
    $(CONFIG_SHELL) scripts/Configure arch/config.in

# make menuconfig
# configure in forms of menutext
menuconfig:
    $(MAKE) -C scripts/lxdialog all
    $(CONFIG_SHELL) scripts/Menuconfig arch/config.in

# make clean
# delete middle files
clean:
    find . \( -name '*.o' -o -name core -o -name ".*.flags" \) -type f -print \
    | grep -v lxdialog/ | xargs rm -f
    $(RM) $(CLEAN_FILES)

# make distclean
# delete middle files and configure files
distclean: Version clean
    $(RM) $(DISTCLEAN_FILES)

# make myboard_config
# Configuration targets. Use these to select a
# configuration for your architecture
#
# here, @ makes the command no echo back.and "CFG=$(@:_config=)" uses the advanced
# function of variables.for example,
#     foo:=a.o b.o c.o
#     bar:=$(foo:.o=.c)
# so bar = a.c b.c c.c
%_config:
    @ (                                                   \
    CFG=$(@:_config=);                                    \
    if [ -f arch/def-configs/$$CFG ]; then                \
     [ -f .config ] && mv -f .config .config.old;         \
     cp arch/def-configs/$$CFG .config;                   \
     echo "*** Default configuration for $$CFG installed";\
     echo "*** Next, you may run 'make oldconfig'";       \
    else                                                  \
     echo "$$CFG does not exist";                         \
    fi;                                                   \
    )

# make myboard
# make your work automatically
%: ./arch/def-configs/%
    $(MAKE) distclean
    cp arch/def-configs/$* ./.config -f
    $(MAKE) oldconfig
    $(MAKE)

#
# Section 9: real handle prerequisites
#

# read SUBDIRS
# here SUBDIRS = drivers lib
linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))

# read sub Makefile in SUBDIRS
$(patsubst %, _dir_%, $(SUBDIRS)) :
    $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)

# create [include/compile.h]
include/compile.h: $(CONFIGURATION)
    @echo \#define VIVI_RELEASE \"$(VIVIRELEASE)\" > .ver
    @echo -n \#define UTS_VERSION \"\#$(VIVIRELEASE) >> .ver
    @if [ -f .name ]; then echo -n \-`cat .name` >> .ver; fi
    @echo ' '`date`'"' >> .ver
    @echo \#define VIVI_COMPILE_BY \"`whoami`\" >> .ver

    @echo \#define VIVI_COMPILE_HOST \"`hostname`\" >> .ver
    @echo \#define VIVI_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
    @mv -f .ver $@

# compile [init/version.c]
init/version.o: init/version.c include/compile.h
    $(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c $< -o $@

# compile [init/main.c]
init/main.o: init/main.c
    $(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c $< -o $@

# create TAGS
TAGS:
    etags `find include -name '*.h'`
    find $(SUBDIRS) init -name '*.[ch]' | xargs etags -a

# Exuberant ctags works better with -I
tags:
    CTAGSF=`ctags --version | grep -i exuberant >/dev/null &&            \
        echo "-I __initdata,__exitdata,EXPORT_SYMBOL,EXPORT_SYMBOL_NOVERS"`;     \
    ctags $$CTAGSF `find include -name '*.h'` &&                    \
    find $(SUBDIRS) init -name '*.[ch]' | xargs ctags $$CTAGSF -a

#
# Section 10: default rules
#

include Rules.make


#

# Section 11: debug
#

debug:
    @echo "VIVIRELEASE: $(VIVIRELEASE)"
    @echo "ARCH: $(ARCH)"
    @echo "CONFIG_SHELL: $(CONFIG_SHELL)"
    @echo "CROSS_COMPILE: $(CROSS_COMPILE)"
    @echo "TOPDIR: $(TOPDIR)"
    @echo "SUBDIRS: $(SUBDIRS)"
    @echo "CONFIGURATION: $(CONFIGURATION)"
    @echo "DRIVERS: $(DRIVERS)"

    修改的幅度比较大,但是这样组织之后,清晰了很多,至少我是这样感觉。现在一般的命令处理有:

    make clean:清理中间生成文件,不包括配置文件

    make distclean:清理所有生成文件

    make config:文本配置,如果没有[.config],默认读取[arch/defconfig]

    make oldconfig: 文本配置,默认读取[.config]

    make menuconfig:图像配置,需要图形库的支持

    make debug:变量调试

    make vivi:编译,生成目标文件

    make myboard_config:假设我的配置保存为[arch/def-configs/myboard],那么此命令的结果是把该配置复制到[.config]。如果原来存在[.config],则把原来的[.config]更改为[.config.old].

    make myboard:假设我的配置保存为[arch/def-configs/myboard],那么此命令实现了自动化编译。首先清理所有的生成文件,然后把myboard读取到[.config],根据此配置编译生成最终的vivi目标文件。

    我常用的做法是:(1)make smdk2410_config(2)make menuconfig(3)make vivi(4)cp .config ./arch/def-configs/myboard.如果以后改变程序,而不改变配置,就可以执行make distclean.理解了整个过程,对这些就可以熟练应用了。也可以自己添加适合自己的编译命令。

    Makefile采用了主Makefile和子Makefile配合的方式,它们之间的联系如下:

    首先,读取主Makefile,主Makefile通过该"include arch/Makefile"完成包含,主要是一些相关的配置。至于其他的子Makefile,在执行make vivi时,有依赖关系linuxsubdirs,这个部分就是完成读取并完成所有其他子Makefile.具体就在下面语句。

linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))

# read sub Makefile in SUBDIRS
$(patsubst %, _dir_%, $(SUBDIRS)) :
    $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)
   

    所有的显示规则和隐式规则都在Rules.make里,也是用include引入,这样就形成了一条链,完美的管理着你的项目。另外,配置部分需要在另外一篇文档中总结。需要读scripts下的脚本,现在还没有读完。

    相对于vivi原始工程,做了一个patch。

文件: patch.rar
大小: 6KB
下载: 下载

评论

此博客中的热门博文

【转】VxWorks中的地址映射

在运用嵌入式系统VxWorks和MPC860进行通信系统设计开发时,会遇到一个映射地址不能访问的问题。 缺省情况下,VxWorks系统已经进行了如下地址的映射:   memory地址、bcsr(Board Control and Status)地址、PC_BASE_ADRS(PCMCIA)地址、Internal Memory地址、rom(Flach memory)地址等,但是当你的硬件开发中要加上别的外设时,如(falsh、dsp、FPGA等),对这些外设的访问也是通过地址形式进行读写,如果你没有加相应的地址映射,那么是无法访问这些外设的。   和VxWorks缺省地址映射类似,你也可以进行相应的地址映射。   如下是地址映射原理及实现:   1、 地址映射结构 在Tornado\target\h\vmLib.h文件中 typedef struct phys_mem_desc { void *virtualAddr; void *physicalAddr; UINT len; UINT initialStateMask; /* mask parameter to vmStateSet */ UINT initialState; /* state parameter to vmStateSet */ } PHYS_MEM_DESC; virtualAddr:你要映射的虚拟地址 physicalAddr:硬件设计时定义的实际物理地址 len;要进行映射的地址长度 initialStateMask:可以初始化的地址状态: 有如下状态: #define VM_STATE_MASK_VALID 0x03 #define VM_STATE_MASK_WRITABLE 0x0c #define VM_STATE_MASK_CACHEABLE 0x30 #define VM_STATE_MASK_MEM_COHERENCY 0x40 #define VM_STATE_MASK_GUARDED 0x80 不同的CPU芯片类型还有其特殊状态 initialState:实际初始化的地址状态: 有如下状态: #define VM_STATE_VALID 0x01 #define VM_STATE_VALID_NOT 0x00 #define VM_STATE_WRITA

【转】cs8900网卡的移植至基于linux2.6内核的s3c2410平台

cs8900网卡的移植至基于linux2.6内核的s3c2410平台(转) 2008-03-11 20:58 硬件环境:SBC-2410X开发板(CPU:S3C2410X) 内核版本:2.6.11.1 运行环境:Debian2.6.8 交叉编译环境:gcc-3.3.4-glibc-2.3.3 第一部分 网卡CS8900A驱动程序的移植 一、从网上将Linux内核源代码下载到本机上,并将其解压: #tar jxf linux-2.6.11.1.tar.bz2 二、打开内核顶层目录中的Makefile文件,这个文件中需要修改的内容包括以下两个方面。 (1)指定目标平台。 移植前:         ARCH?= $(SUBARCH) 移植后: ARCH            :=arm (2)指定交叉编译器。 移植前: CROSS_COMPILE ?= 移植后: CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 注:这里假设编译器就放在本机的那个目录下。 三、添加驱动程序源代码,这涉及到以下几个方面。(1)、从网上下载了cs8900.c和cs8900.h两个针对2.6.7的内核的驱动程序源代码,将其放在drivers/net/arm/目录下面。 #cp cs8900.c ./drivers/net/arm/ #cp cs8900.h ./drivers/net/arm/ 并在cs8900_probe()函数中,memset (&priv,0,sizeof (cs8900_t));函数之后添加如下两条语句: __raw_writel(0x2211d110,S3C2410_BWSCON); __raw_writel(0x1f7c,S3C2410_BANKCON3); 注:其原因在"第二部分"解释。 (2)、修改drivers/net/arm/目录下的Kconfig文件,在最后添加如下内容: Config ARM_CS8900    tristate "CS8900 support" depends on NET_ETHERNET && A

【转】多迷人Gtkmm啊

前边已经说过用glade设计界面然后动态装载,接下来再来看看怎么改变程序的皮肤(主题)     首先从 http://art.gnome.org/themes/gtk2 下载喜欢的主题,从压缩包里提取gtk-2.0文件夹让它和我们下边代码生成的可执行文件放在同一个目录下,这里我下载的的 http://art.gnome.org/download/themes/gtk2/1317/GTK2-CillopMidnite.tar.gz     然后用glade设计界面,命名为main.glade,一会让它和我们下边代码生成的可执行程序放在同一个目录下边     然后开始写代码如下: //main.cc #include <gtkmm.h> #include <libglademm/xml.h> int main(int argc, char *argv[]) {     Gtk::Main kit(argc,argv);         Gtk::Window *pWnd;        gtk_rc_parse("E:\\theme-viewer\\themes\\gtk-2.0\\gtkrc");       Glib::RefPtr<Gnome::Glade::Xml> refXml;     try     {         refXml = Gnome::Glade::Xml::create("main.glade");     }     catch(const Gnome::Glade::XmlError& ex)     {         Gtk::MessageDialog dialog("Load glade file failed!", false,       \                                   Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);         dialog.run();               return 1;     }         refXml->get_widget("main", pWnd);     if(pW