跳至主要内容

【转】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
下载: 下载

评论

此博客中的热门博文

【转】AMBA、AHB、APB总线简介

AMBA 简介 随着深亚微米工艺技术日益成熟,集成电路芯片的规模越来越大。数字IC从基于时序驱动的设计方法,发展到基于IP复用的设计方法,并在SOC设计中得到了广泛应用。在基于IP复用的SoC设计中,片上总线设计是最关键的问题。为此,业界出现了很多片上总线标准。其中,由ARM公司推出的AMBA片上总线受到了广大IP开发商和SoC系统集成者的青睐,已成为一种流行的工业标准片上结构。AMBA规范主要包括了AHB(Advanced High performance Bus)系统总线和APB(Advanced Peripheral Bus)外围总线。   AMBA 片上总线        AMBA 2.0 规范包括四个部分:AHB、ASB、APB和Test Methodology。AHB的相互连接采用了传统的带有主模块和从模块的共享总线,接口与互连功能分离,这对芯片上模块之间的互连具有重要意义。AMBA已不仅是一种总线,更是一种带有接口模块的互连体系。下面将简要介绍比较重要的AHB和APB总线。 基于 AMBA 的片上系统        一个典型的基于AMBA总线的系统框图如图3所示。        大多数挂在总线上的模块(包括处理器)只是单一属性的功能模块:主模块或者从模块。主模块是向从模块发出读写操作的模块,如CPU,DSP等;从模块是接受命令并做出反应的模块,如片上的RAM,AHB/APB 桥等。另外,还有一些模块同时具有两种属性,例如直接存储器存取(DMA)在被编程时是从模块,但在系统读传输数据时必须是主模块。如果总线上存在多个主模块,就需要仲裁器来决定如何控制各种主模块对总线的访问。虽然仲裁规范是AMBA总线规范中的一部分,但具体使用的算法由RTL设计工程师决定,其中两个最常用的算法是固定优先级算法和循环制算法。AHB总线上最多可以有16个主模块和任意多个从模块,如果主模块数目大于16,则需再加一层结构(具体参阅ARM公司推出的Multi-layer AHB规范)。APB 桥既是APB总线上唯一的主模块,也是AHB系统总线上的从模块。其主要功能是锁存来自AHB系统总...

【转】smb协议栈使用示例

/*  * * uncdownload.c  * *  * * Utility for downloading files from SMB shares using libsmbclient  * *  * * Copyright(C) 2006 Sophos Plc, Oxford, England.  * *  * * This program is free software; you can redistribute it and/or modify it under the terms of the  * * GNU General Public License Version 2 as published by the Free Software Foundation.  * *  * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without  * * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  * * See the GNU General Public License for more details.  * *  * * You should have received a copy of the GNU General Public License along with this program; if not,  * * write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA  * *  * */ # include < sys / types . h > # include < sys / time . h > # include ...

【转】udp编程实例

UDP通讯实例 2008-04-29 15:30:05 / 个人分类: linux C编程 UDP协议的几点好处: 1.UDP不要求保持一个连接; 2.UDP没有因接收方认可收到数据包(或者当数据包没有正确抵达而自动重传)而带来的开销; 3.设计UDP的目的是用于短应用和控制信息; 4.在一个数据包接一个数据包的基础上,UDP要求的 网络 带宽比TCP更小。 UDP的几个缺点: 1.程序员必须创建代码检测传输错误并进行重传(如果应用程序要求这样做); 2.程序员必须把大数据包分片。 code: <1> /* * sender.c--UDP protocol example */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> int port = 6789; int main() {     int socket_descrīptor;     int iter = 0;     char buf[80];     struct sockaddr_in address;     /* Initialize socket address structure for Interner Protocols */     bzero(&address, sizeof(address)); // empty data structure     address.sin_family = AF_INET;     address.sin_addr.s_addr = inet_addr("127.0.0.1");     address...