uboot顶层makefile详解2

uboot顶层makefile详解2
uboot顶层makefile详解2

# (C) Copyright 2000-2006 版权所有:2000~2006

# Wolfgang Denk, DENX Software Engineering, wd@denx.de. Wolfgang Denk一个开发人员的名字DENX Software Engineering公司名称wd@denx.de邮箱

#

# See file CREDITS for list of people who contributed to this 可以看看CREDITS,里面有为uboot做出贡献的所有开发人员

# project.

#

# This program is free software; you can redistribute it and/or 这个程序是一个自由软件,你可以重新发布它,或者在自由软件基金组织所颁布的

# modify it under the terms of the GNU General Public License as GNU GPL(GNU 公共许可协议)的前提下修改它;注意,不论是第二版的GPL还是

# published by the Free Software Foundatio; either version 2 of 任何更新的版本,都可以,这由你选择。

# the License, or (at your option) any later version.

#

# 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 GPL。

# GNU General Public License for more details.

#

# Y ou should have received a copy of the GNU General Public License 你应该已经得到了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

#

VERSION = 1

PA TCHLEVEL = 1

SUBLEVEL = 6

EXTRA VERSION =

U_BOOT_VERSION = $(VERSION).$(PA TCHLEVEL).$(SUBLEVEL)$(EXTRA VERSION) #1.1.6版

VERSION_FILE = $(obj)include/version_autogenerated.h #版本文件

# uname 命令将正在使用的操作系统名写到标准输出中

# -m 显示硬件运行系统的机器ID 号

# 定义变量,HOSTARCH里面存储的是机器ID号,即主机架构类型

HOSTARCH := $(shell uname -m | \ #可以把shell脚本写到makefile里面,这是make中的shell function,相当于shell中的命令替换。

sed -e s/i.86/i386/ \ #shell uname -m 获得机器硬件名称

-e s/sun4u/sparc64/ \ #sed -e s/arm.*/arm/ 的意思是把前缀为arm的所有模式替换为arm。结合前面的uname命令来理解就是:

-e s/arm.*/arm/ \ #把uname -m的结果(主机架构类型或者称为机器ID 号)通过管道传递给sed命令,然后把前缀为arm的所有模式替换为arm。

-e s/sa110/arm/ \ #sed的语法:sed [ -n ] Script [ File ... ]

-e s/powerpc/ppc/ \ # sed [ -n ] [ -e Script ] ... [ -f ScriptFile ] ... [ File ... ]

-e s/macppc/ppc/) #sed 命令根据编辑脚本,去修改指定的File 文件(这里file是作为一个输入参数的)的行,并将其写到标准输出。

#sed 命令包含很多功能,用于选择要修改的行(请注意,sed是针对行进行操作的),并只对选择的行作更改。

#sed 命令使用两个工作空间来保留修改的行:保留选定行的"模式空间" 和暂时存储行的"保留空间"。

#这里的编辑脚本由单独的子命令构成,每个单独的行对应着一个子命令。sed子命令的格式如下:[address-range] function[modifiers],即:[地址范围] 函数[修改符]

#sed 命令通过将一个输入行读入模式空间,依次应用所有的sed 子命令(这些子命令的地址选择了该行),

#并将模式空间写到标准输出来处理每个输入的File 输入文件(file就是输入参数)。然后清除模式空间,并对输入的File 中指定的每行重复该过程。

#一些sed 子命令使用保留空间来保存后继检索的所有的,或部分的模式空间。

#当命令包含地址(行号或搜索模式)时,该命令只会对被寻址的行起作用。否则,该命令适用于所有的行。

#注意,这里的参数"-e"的意思是:使用Script 变量作为编辑脚本。如果你只使用一个-e 标志并且不使用-f 标志,则可以省略-e 标志。

#注意,这里的"sed -e s/arm.*/arm/"表示把前缀为arm的所有模式替换为arm,也就是sed s/pattern/replacement/flags

#用replacement 字符串代替在模式空间中首次出现的pattern 参数。除了空格或换行符,在s 子命令之后显示的任何字符都能代替/(斜杠)分隔符。

#uname -s 表示:显示系统名,标志缺省为开,即uname命令不带任何参数的输出和加参数-s输出相同,即Linux

#tr是一个shell命令,可以实现许多sed的功能,这里tr '[:upper:]' '[:lower:]'的意思是把管道中的Linux 中的大写字母L 转换成小写字母l

#下面这句话的意思是定义变量HOSTOS,HOSTOS里面存放的是主机安装的,并且当前正在运行的操作系统。

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \

sed -e 's/\(cygwin\).*/cygwin/') # 这一句的意思是检测出主机安装的,并且当前正在运行的操作系统名,并把这个系统名中的大写字母转换为小写字母,

# 然后在通过sed流编辑器匹配所有的,这个系统名中出现的"\(cygwin\).*"模式,然后再用"cygwin"模式替换。

#export HOSTARCH HOSTOS 的意思是输出两个makefile变量HOSTARCH和HOSTOS export HOSTARCH HOSTOS

# Deal with colliding definitions from tcsh etc. 用来处理来自tcsh的互相冲突的定义等等

# 一般来说,shell可以分成两类。第一类是由Bourne shell衍生出来的包括

# sh,ksh,bash,与zsh。第二类是由C shell衍生出来的,包括csh与

# tcsh。除此之外还有一个rc,有人认为该自成一类,有人认为该归类在Bourne shell。VENDOR = #开发商

#########################################################################

#

# U-boot build supports producing a object files to the separate external

# U-boot 的编译过程可以支持向一个自己定义的路径生成最终的目标文件

# directory. Two use cases are supported:

# 这里提供了两种用法:

#

# 1) Add O= to the make command line #第一种用法:通过在终端执行命令make O=/dir(即你指定的生成的目标文件的存放目录)

# 'make O=/tmp/build all'

#

# 2) Set environement variable BUILD_DIR to point to the desired location #第二种用法:通过设置环境变量来指定目标文件存放目录,如下所示:

# 'export BUILD_DIR=/tmp/build'

# 'make'

#

# The second approach can also be used with a MAKEALL script #第二种方法也可以写成一个MAKEALL脚本,然后执行MAKEALL,如下所示:

# 'export BUILD_DIR=/tmp/build'

# './MAKEALL'

#

# Command line 'O=' setting overrides BUILD_DIR environent variable. #命令行'O='设置会覆盖环境变量BUILD_DIR的设置

#

# When none of the above methods is used,the local build is performed and #如果都不采用上面两种方法,那么目标文件放到源码顶层目录,也就是U-BOOT顶层目录

# the object files are placed in the source directory.

#理解了上面一段英文,这里就不难理解了

#方法1:

ifdef O #如果变量'O' 已经被定义过

ifeq ("$(origin O)", "command line") #如果变量'O' 在命令行中定义过

BUILD_DIR := $(O) #就把变量'O' 的值(目标文件存放目录)赋给BUILD_DIR

endif

endif

#方法2

ifneq ($(BUILD_DIR),) #如果变量BUILD_DIR不为空,即环境变量BUILD_DIR 被定义过

saved-output := $(BUILD_DIR) #那么把它的值赋给saved-output

# Attempt to create a output directory. #生成一个输出路径,即目标文件存放目录BUILD_DIR

$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

#shell [ -d ${BUILD_DIR} ] 是什么意思?是不是生成一个目录的意思?

# V erify if it was successful. 测试目录是否创建成功

BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd) #这又是什么意思,说明对shell还不够理解!

$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))

#这里用了一个if函数,意思是如果如果$(BUILD_DIR) 非空,则什么都不执行(返回空),否则执行error函数,输出错误信息

endif

# ifneq ($(BUILD_DIR),) #意思是:如果没有定义目标文件存放目录

#Makefile中定义了源码以及生成目标文件存放的目录,目标文件存放目录BUILD_DIR可以通过make O=dir指定。如果没有指定,则设定为源码顶层目录。

#一般编译的时候不指定输出目录,则BUILD_DIR为空。其他目录变量如下:

OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)) #如果$(BUILD_DIR)不为空,则返回$(BUILD_DIR),并赋给OBJTREE,即自己定制的目标存放目录

SRCTREE := $(CURDIR) #把当前源码所在目录$(CURDIR) 赋给SRCTREE

TOPDIR := $(SRCTREE) #把当前源码所在目录$(CURDIR) 赋给SRCTREE

LNDIR := $(OBJTREE) #存放生成的目录文件

export TOPDIR SRCTREE OBJTREE

MKCONFIG := $(SRCTREE)/mkconfig #MKCONFIG指向源码所在目录(U-BOOT顶层目录)下的mkconfig配置文件

export MKCONFIG

#在编译UBOOT之前,我们先要执行:

#make smdk2410_config

#从本Makefile的下文可以判断出smdk2410_config为Makefile的一个目标。

#smdk2410_config: unconfig的意思是为smdk2410开发板建立一个编译项。

#显然,执行#make smdk2410_config时,先执行unconfig目标(不指定输出目标时,obj,src变量均为空),unconfig下面的命令主要任务是清理上

#一次执行make *_config时生成的头文件和makefile的包含文件。

#主要是include/config.h和include/config.mk

#然后执行命令:@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

#arm 表示CPU的构架是基于ARM体系的

#arm920t 表示CPU的类型是arm920t

#smdk2410 表示开发板的型号

#NULL 表示开发商或者经销商的名称,这里为空

#s3c24x0 表示基于S3C2410的片上系统

#MKCONFIG指向UBOOT顶层目录下的mkconfig脚本配置文件,后面五个字符串是传入的参数(好像$(@:_config=)也是一个参数)。

#下面来分析一下mkconfig这个脚本配置文件,点击链接:https://www.360docs.net/doc/c16199397.html,/blog/static/120446842010325102158182/

ifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时

REMOTE_BUILD := 1 #定义变量REMOTE_BUILD := 1 这个变量算是一个flag吧

export REMOTE_BUILD

endif

# $(obj) and (src) are defined in config.mk(顶层目录下)but here in main Makefile

# we also need them before config.mk is included which is the case for

# some targets like unconfig, clean, clobber, distclean, etc.

# $(obj) and $(src)都被定义在顶层目录下的config.mk脚本配置文件里面,

# 但是在这个主Makefile里面,我们同样需要他们,

# 因为在主Makefile文件包含config.mk之前,$(obj) and $(src)偶尔地会成为这些目标的case: # unconfig, clean, clobber, distclean, etc

ifneq ($(OBJTREE),$(SRCTREE)) #当目标存放目录不是U-BOOT顶层目录(源码目录)时

obj := $(OBJTREE)/ #定义变量obj,让其等于目标存放目录

src := $(SRCTREE)/ #定义变量src,让其等于uboot顶层目录

else

obj := #否则,这两个变量都定义为空

src :=

endif

export obj src

#########################################################################

ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)) #这句话让人费解!

# 通配符在规则中可以自动扩展,但设置在变量中或在函数的参数中通配符一般不能正常扩展。

# 如果您需要在这些场合扩展通配符,您应该使用函数wildcard,格式如下:

# $(wildcard pattern...)

# load ARCH, BOARD, and CPU configuration

#加载ARCH, BOARD, and CPU 配置

include $(OBJTREE)/include/config.mk # 这时候,才开始包含/include/config.mk的

export ARCH CPU BOARD VENDOR SOC

#指定交叉编译器前缀

ifeq ($(ARCH),arm)

CROSS_COMPILE = arm-linux-

#这里你可以把交叉编译器的安装路径加到arm-linux-之前,比如你的交叉编译器安装路径是/root/u-boot/usr/local/arm/3.3.2/bin/

#你可以这样定义CROSS_COMPILE = /root/u-boot/usr/local/arm/3.3.2/bin/arm-linux-

#这样一来,你在终端进行编译的时候就不用指定CROSS_COMPILE=arm-linux-了

#但请注意:在编译内核的时候,交叉编译器必须安装在/usr/local/arm下,否则会发生错误!!!!!

endif

export CROSS_COMPILE

# load other configuration 加载其他设置,这里是包含顶层目录下的config.mk配置文件,这个文件主要做了三个工作:

# 1、定义了交叉编译器2、定义了编译选项3、定义了编译规则

# 对本文件具体的分析,请查看链接:

https://www.360docs.net/doc/c16199397.html,/blog/static/12044684201032541139914/

include $(TOPDIR)/config.mk

#########################################################################

# U-Boot objects....order is important (i.e. start must be first)

# uboot目标...书写顺序很重要,比如start.o必须排在第一位

######################################################################### OBJS = cpu/$(CPU)/start.o

#start.o必须放在目标文件的第一位,因为uboot执行的第一段代码就是start.S

#具体原因可以查看链接脚本/u-boot-1.1.6/board/smdk2410/u-boot.lds,点击连接:

https://www.360docs.net/doc/c16199397.html,/blog/static/120446842010320101137932/

.....................................

OBJS := $(addprefix $(obj),$(OBJS)) #这句的意思是把目标文件存放路径以前缀的形式加到start.O之前,然后再赋给OBJS

#以下是编译UBOOT需要的库文件

LIBS = lib_generic/libgeneric.a

LIBS += board/$(BOARDDIR)/lib$(BOARD).a # 严重平台依赖的

LIBS += cpu/$(CPU)/lib$(CPU).a # 严重平台依赖的

ifdef SOC

LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a # 严重平台依赖的

endif

LIBS += lib_$(ARCH)/lib$(ARCH).a # 严重平台依赖的

LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \

fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a

LIBS += net/libnet.a

LIBS += disk/libdisk.a

LIBS += rtc/librtc.a

LIBS += dtt/libdtt.a

LIBS += drivers/libdrivers.a

LIBS += drivers/nand/libnand.a

LIBS += drivers/nand_legacy/libnand_legacy.a

LIBS += drivers/sk98lin/libsk98lin.a

LIBS += post/libpost.a post/cpu/libcpu.a

LIBS += common/libcommon.a

LIBS += $(BOARDLIBS)

LIBS := $(addprefix $(obj),$(LIBS))

.PHONY : $(LIBS) # 这是一个伪目标

#根据所生成的include/config.mk文件定义的几个变量ARCH, CPU, BOARD, SOC,我们可以

#确定硬件平台依赖的目录文件。smdk2410平台相关(依赖)目录以及对应生成的库文件如下:

#board/smdk2410/: 库文件board/smdk2410/libsmdk2410.a

#cpu/arm920t/: 库文件cpu/arm920t/libarm920t.a

#cpu/arm920t/s3c24x0: 库文件cpu/arm920t/s3c24x0/libs3c24x0.a

#lib_arm:库文件lib_arm/libarm.a

#include/asm-arm: 头文件

#include/cnofigs/smdk2410.h:头文件

# Add GCC lib

PLA TFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

# The "tools" are needed early, so put this first

# Don't include stuff already done in $(LIBS) 不要包含已经在$(LIBS) 中的任何东西#

# 伪目标SUBDIRS:用于执行tools、examples、post、post\cpu子目录下的make文件SUBDIRS = tools \

examples \

post \

post/cpu

.PHONY : $(SUBDIRS)

ifeq ($(CONFIG_NAND_U_BOOT),y)

NAND_SPL = nand_spl

U_BOOT_NAND = $(obj)u-boot-nand.bin

endif

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBS))

#########################################################################

#########################################################################

#这里是最终要生成的各种镜像文件u-boot.hex、u-boot.srec、u-boot.bin、System.map、u-boot.img

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

all: $(ALL)

$(obj)u-boot.hex: $(obj)u-boot

$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ # $<,$@ 分别表示使用该规则的源文件和目标文件

$(obj)u-boot.srec: $(obj)u-boot

$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

$(obj)u-boot.bin: $(obj)u-boot

$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

$(obj)u-boot.img: $(obj)u-boot.bin

./tools/mkimage -A $(ARCH) -T firmware -C none \

-a $(TEXT_BASE) -e 0 \

-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \

sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \

-d $< $@

$(obj)u-boot.dis: $(obj)u-boot

$(OBJDUMP) -d $< > $@

#此处生成的是uboot的ELF文件镜像

$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\

cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \

--start-group $(__LIBS) --end-group $(PLA TFORM_LIBS) \

-Map u-boot.map -o u-boot

#依赖目标$(OBJS),也就是cpu/start.o

$(OBJS):

$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

#依赖目标$(LIBS),目标很多,都是每个子目录的库文件*.a,通过执行相应子目录下的make 来完成

$(LIBS):

$(MAKE) -C $(dir $(subst $(obj),,$@))

#这里解释一下这个makefile函数$(dir names...)

#抽取‘names’中每一个文件名的路径部分,文件名的路径部分包括从文件名的开始到最后一个斜杠(含斜杠)

#之前的一切字符。如果文件名中没有斜杠,路径部分是‘./’。如:

#$(dir src/foo.c hacks)

#产生的结果为‘src/ ./’。

$(SUBDIRS):

$(MAKE) -C $@ all

$(NAND_SPL): version

$(MAKE) -C nand_spl/board/$(BOARDDIR) all

$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin

cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin

#依赖目标version:生成版本信息到版本文件VERSION_FILE中

version:

@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \ echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \

echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \

$(TOPDIR)) >> $(VERSION_FILE); \

echo "\"" >> $(VERSION_FILE)

gdbtools:

$(MAKE) -C tools/gdb all || exit 1

updater:

$(MAKE) -C tools/updater all || exit 1

env:

$(MAKE) -C tools/env all || exit 1

#依赖目标depend:生成各个子目录.depend文件,.depend列出每个目标文件的依赖文件。#生成方法:调用每个子目录的make_depend

depend dep:

for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

tags ctags:

ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \

lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \

fs/cramfs fs/fat fs/fdos fs/jffs2 \

net disk rtc dtt drivers drivers/sk98lin common \

\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

etags:

etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \

lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \

fs/cramfs fs/fat fs/fdos fs/jffs2 \

net disk rtc dtt drivers drivers/sk98lin common \

\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`

$(obj)System.map: $(obj)u-boot

@$(NM) $< | \

grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \

sort > $(obj)System.map

######################################################################### else

all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \

$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \

$(SUBDIRS) version gdbtools updater env depend \

dep tags ctags etags $(obj)System.map:

@echo "System not configured - see README" >&2

@ exit 1

endif

.PHONY : CHANGELOG

CHANGELOG:

手动建立makefile简单实例解析

手动建立makefile简单实例解析 假设我们有一个程序由5个文件组成,源代码如下:/*main.c*/ #include "mytool1.h" #include "mytool2.h" int main() { mytool1_print("hello mytool1!"); mytool2_print("hello mytool2!"); return 0; } /*mytool1.c*/ #include "mytool1.h" #include void mytool1_print(char *print_str) { printf("This is mytool1 print : %s ",print_str); } /*mytool1.h*/ #ifndef _MYTOOL_1_H #define _MYTOOL_1_H void mytool1_print(char *print_str); #endif /*mytool2.c*/ #include "mytool2.h" #include void mytool2_print(char *print_str) { printf("This is mytool2 print : %s ",print_str); }

/*mytool2.h*/ #ifndef _MYTOOL_2_H #define _MYTOOL_2_H void mytool2_print(char *print_str); #endif 首先了解一下make和Makefile。GNU make是一个工程管理器,它可以管理较多的文件。我所使用的RedHat 9.0的make版本为GNU Make version 3.79.1。使用make的最大好处就是实现了“自动化编译”。如果有一个上百个文件的代码构成的项目,其中一个或者几个文件进行了修改,make就能够自动识别更新了的文件代码,不需要输入冗长的命令行就可以完成最后的编译工作。make执行时,自动寻找Makefile(makefile)文件,然后执行编译工作。所以我们需要编写Makefile文件,这样可以提高实际项目的工作效率。 在一个Makefile中通常包含下面内容: 1、需要由make工具创建的目标体(target),通常是目标文件或可执行文件。 2、要创建的目标体所依赖的文件(dependency_file)。 3、创建每个目标体时需要运行的命令(command)。 格式如下: target:dependency_files command target:规则的目标。通常是程序中间或者最后需要生成的文件名,可以是.o文件、也可以是最后的可执行程序的文件名。另外,目标也可以是一个make执行的动作的名称,如目标“clean”,这样的目标称为“伪目标”。 dependency_files:规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。 command:规则的命令行。是make程序所有执行的动作(任意的shell命令或者可在shell下执行的程序)。一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]字符开始,[Tab]字符告诉make此行是一个命令行。make按照命令完成相应的动作。这也是书写Makefile中容易产生,而且比较隐蔽的错误。命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可以没有依赖而只有动作(指定的命令)。比如Makefile中的目标“clean”,此目标没有依赖,只有命令。它所指定的命令用来删除make过程产生的中间文件(清理工作)。 在Makefile中“规则”就是描述在什么情况下、如何重建规则的目标文件,通常规则

u-boot启动分析

背景: Board →ar7240(ap93) Cpu →mips 1、首先弄清楚什么是u-boot Uboot是德国DENX小组的开发,它用于多种嵌入式CPU的bootloader程序, uboot不仅支持嵌入式linux系统的引导,当前,它还支持其他的很多嵌入式操作系统。 除了PowerPC系列,还支持MIPS,x86,ARM,NIOS,XScale。 2、下载完uboot后解压,在根目录下,有如下重要的信息(目录或者文件): 以下为为每个目录的说明: Board:和一些已有开发板有关的文件。每一个开发板都以一个子目录出现在当前目录中,子目录存放和开发板相关的配置文件。它的每个子文件夹里都有如下文件(以ar7240/ap93为例): Makefile Config.mk Ap93.c 和板子相关的代码 Flash.c Flash操作代码 u-boot.lds 对应的链接文件 common:实现uboot命令行下支持的命令,每一条命令都对应一个文件。例如bootm命令对应就是cmd_bootm.c cpu:与特定CPU架构相关目录,每一款Uboot下支持的CPU在该目录下对应一个子目录,比如有子目录mips等。它的每个子文件夹里都有入下文件: Makefile Config.mk Cpu.c 和处理器相关的代码s Interrupts.c 中断处理代码 Serial.c 串口初始化代码 Start.s 全局开始启动代码 Disk:对磁盘的支持

Doc:文档目录。Uboot有非常完善的文档。 Drivers:Uboot支持的设备驱动程序都放在该目录,比如网卡,支持CFI的Flash,串口和USB等。 Fs:支持的文件系统,Uboot现在支持cramfs、fat、fdos、jffs2和registerfs。 Include:Uboot使用的头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的文件。该目下configs目录有与开发板相关的配置文件,如 ar7240_soc.h。该目录下的asm目录有与CPU体系结构相关的头文件,比如说mips 对应的有asm-mips。 Lib_xxx:与体系结构相关的库文件。如与ARM相关的库放在lib_arm中。 Net:与网络协议栈相关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。 Tools:生成Uboot的工具,如:mkimage等等。 3、mips架构u-boot启动流程 u-boot的启动过程大致做如下工作: 1、cpu初始化 2、时钟、串口、内存(ddr ram)初始化 3、内存划分、分配栈、数据、配置参数、以及u-boot代码在内存中的位置。 4、对u-boot代码作relocate 5、初始化malloc、flash、pci以及外设(比如,网口) 6、进入命令行或者直接启动Linux kernel 刚一开始由于参考网上代码,我一个劲的对基于smdk2410的板子,arm926ejs的cpu看了N 久,启动过程和这个大致相同。 整个启动中要涉及到四个文件: Start.S →cpu/mips/start.S Cache.S →cpu/mips/cache.S Lowlevel_init.S →board/ar7240/common/lowlevel_init.S Board.c →lib_mips/board.c 整个启动过程分为两个阶段来看: Stage1:系统上电后通过汇编执行代码 Stage2:通过一些列设置搭建了C环境,通过汇编指令跳转到C语言执行. Stage1: 程序从Start.S的_start开始执行.(至于为什么,参考u-boot.lds分析.doc) 先查看start.S文件吧!~ 从_start标记开始会看到一长串莫名奇妙的代码:

linux驱动的Makefile分析

第6行,判断KERNELRELEASE是否为空,该变量是描述内核版本的字符串。只有执行make命令的当前目录为内核源代码目录时,该变量才不为空字符。 第7、8行定义了KERNELDIR和PWD变量。KERNELDIR是内核路径变量,PWD是由执行pwd命令得到的当前模块路径。 第11行make的语法是”Make –C 内核路径M=模块路径modules”,该语法会执行内核模块的编译 第13行是将模块安装到对应的路径中,当在命令执行make modules_install时,执行该命令,其他时候不执行 第24行,意思是将hello.o编译成hello.ko模块。如果要编译其他模块时,只要将hello.o中的hello改成模块的文件名就可以了 Makefile的执行过程: 执行make命令后,将进入Makefile文件。此时KERNELRELEASE变量为空,此时是第一次进入Makefile文件。当执行完变量赋值代码后,会根据make参数执行不同的逻辑。 如下: make modules_install 命令,将执行13、15行将模块安装到操作系统中。 make clean命令,会删除目录中的所有临时文件。 make命令,会执行10、11行编译模块。首先$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 中的-C $(KERNELDIR)选项,会使编译器进入内核源码目录/home/zzc/linux-2.6.31,读取Makefile文件,从中得到一些信息,例如变量KERNELRELEASE将在这里赋值。当内核源码目录中的Makefile文件读取完成后,编译器会根据选项M=$(PWD)第二次进入模块所在的目录,并再一次执行Makefie文件。当第二次执行Makefile文件时,变量KERNELRELEASE

LINUX编程 Makefile中的变量详解应用

第六章:Makefile中的变量 -------------------------------------------------------------------------------- 在Makefile中,变量就是一个名字(像是C语言中的宏),代表一个文本字符串(变量的值)。在Makefile的目标、依赖、命令中引用一个变量的地方,变量会被它的值所取代(与C语言中宏引用的方式相同,因此其他版本的make也把变量称之为“宏”)。在Makefile中变量的特征有以下几点: 1. Makefile中变量和函数的展开(除规则的命令行以外),是在make读取makefile文件时进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的。 2. 变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列表、编译输出的目录列表和所有我们能够想到的事物。 3. 变量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在以后的make版本中被赋予特殊含义,并且这样命名的变量对于一些shell来说不能作为环境变量使用。 4. 变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量。Makefile 传统做法是变量名是全采用大写的方式。推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式,这并不是要求的。但需要强调一点:对于一个工程,所有Makefile中的变量命名应保持一种风格,否则会显得你是一个蹩脚的程序员(就像代码的变量命名风格一样)。 5. 另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。像“$<”、“$@”、“$?”、“$*”等。 6.1 变量的引用 当我们定义了一个变量之后,就可以在Makefile的很多地方使用这个变量。变量的引用方式是:使用“$(VARIABLE_NAME)”或者“${ VARIABLE_NAME }”来引用一个变量的定义。例如:“$(foo) ”或者“${foo}”就是取变量“foo”的值。美元符号“$”在Makefile中有特殊的含义,所有在命令或者文件名中使用“$”时需要用两个美元符号“$$”来表示。对一个变量的引用可以在Makefile的任何上下文中,目标、依赖、命令、绝大多数指示符和新变量的赋值中。这里有一个例子,其中变量保存了所有.o文件的列表: objects = program.o foo.o utils.o program : $(objects) cc -o program $(objects)

嵌入式Linux之我行 史上最牛最详细的uboot移植,不看别后悔

嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(一) 嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux 的朋友提供方便。如有错误之处,谢请指正。 ?共享资源,欢迎转载:https://www.360docs.net/doc/c16199397.html, 一、移植环境 ?主机:VMWare--Fedora 9 ?开发板:Mini2440--64MB Nand,Kernel:2.6.30.4 ?编译器:arm-linux-gcc-4.3.2.tgz ?u-boot:u-boot-2009.08.tar.bz2 二、移植步骤 本次移植的功能特点包括: ?支持Nand Flash读写 ?支持从Nor/Nand Flash启动 ?支持CS8900或者DM9000网卡 ?支持Yaffs文件系统 ?支持USB下载(还未实现) 1.了解u-boot主要的目录结构和启动流程,如下图。

u-boot的stage1代码通常放在cpu/xxxx/start.S文件中,他用汇编语言写成;u-boot的stage2代码通常放在lib_xxxx/board.c文件中,他用C语言写成。各个部分的流程图如下:

2. 建立自己的开发板项目并测试编译。 目前u-boot对很多CPU直接支持,可以查看board目录的一些子目录,如:board/samsung/目录下就是对三星一些ARM 处理器的支持,有smdk2400、smdk2410和smdk6400,但没有2440,所以我们就在这里建立自己的开发板项目。 1)因2440和2410的资源差不多,主频和外设有点差别,所以我们就在board/samsung/下建立自己开发板的项目,取名叫my2440 2)因2440和2410的资源差不多,所以就以2410项目的代码作为模板,以后再修改

Makefile下编写Helloworld的例子

什么是makefile?或许很多Windows的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得 要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专 业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile, 从一个侧面说明了一个人是否具备完成大型工程的能力。 因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中, makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复 杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。 makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make 命令,整个工程完全自动编译,极大的提高了软件 开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如: Delphi的make,VisualC++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。 更新版本 hello.c程序 #include int main(){printf("Hello,World!\n");

return 0;}=== makefile开始=== Helloworld: hello.o gcc hello.o–o Helloworld Hello.o: hello.c hello.h gcc–MM hello.c gcc–c hello.c–o hello.o .PHONY: clean Clean: rm–rf*.o hellworld === makefile结束===

MakeFile

MAKE 的使用 当编译单个文件时,使用GCC已经绰绰有余,但对于有几十个甚至上百个源文件的大工程来讲,单纯用GCC命令进行编译肯定就不行了,那样编译一次太麻烦,这就要求有一种自动化的方法,于是在Linux系统中Make工具就诞生了。 1、什么是Make makefile shell 什么是Make,可以将Make理解为一种脚本,这种脚本主要是用于多文件编译,在传统的命令行式编译方式中,如果修改了工程中的某一个头文件,有时候不需要重新编译整个工程,而只需要编译与这个头文件相关联的源文件即可,但如何以手动的方式从一个大工程里将这些文件找出,并手动编译,是件很麻烦的事情。 为了解决这一问题,设计了Make,make程序可以维护具有相互依赖性的源文件,当某些文件发生改变时,它能自动识别出,并只对相应文件进行自动编译。 虽然make工具有很多智能识别机制,但它无法自动了解他所面对的工程源文件的组成以及依赖规则,这些都需要开发人员编写makefile脚本,告诉make工具。MakeFile编写好以后,就可以在每次修改源文件之后,执行make命令就好了。Makefile make 什么又是makefile了,很多windows的程序开发人员都不知道这个东西,因为那些Windows的IDE都为你做了这些工作,但我觉得要作为一个好的和专业的程序员,makefile还是要懂得,这就好像在有这么多的HTML的编辑器,但如果你想成为一个专业人士,就必须了解HTML的标识的含义。特别在Linux下的软件编译,你就必须自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成某个大型大工程的能力。 2、makefile的组成 一个完整的makefile文件通常由五个部分组成: ◆显示规则 显示规则是指主动编写描述规则,用于指示在何种状态下更新哪些目标文件,即编写makefile时需要明确指出各目标文件所依赖的源文件的集合,以及编 译本目标文件所需的命令。 ◆隐含规则 指用make中默认的编译方式进行编译,即make工具可以根据目标文件的类型自动推导出的规则(由于我们的make有自动推导的功能,所以隐晦的规则可以 让我们比较粗糙地简略书写Makefile ,这是由make所支持的) abc.o abc.c cc -o abc.o -c abc.c ◆变量定义 为提升语句的灵活性,在make脚本中可以使用变量,来代表一个字符串,一组编译命令或一组文件名。(在makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点类似于C语言中的宏,当makefile 被执行时,其中的变量都会被扩展到相应的引用位置上) ◆makefile指示符 指示符告诉make工具,当程序在读取makefile文件时要执行的动作。 (文件指示包括三部分,一个是在一个makefile中引用另外一个makefile,就像C语言中的include一样,另一个是指根据某些情况制定MakeFile中的有效部分,就像C 语言中的预编译#if一样,还有就是定义一个多行的命令,)

make_Makefile 结构分析

Makefile结构分析 -----uClinux (2.6.x内核)系统映像过程 刘江欧阳昭暐吕熙隆 1、源代码文件及目录构成 解压缩uClinux-dist-20070130.tar.gz压缩文件,在uClinux-dist原始子目录下主要有:config、Bin、linux-2.4.x、linux-2.6.x 、lib、tools、Include、user和vendors,以及文件Makefile。另外,在编译后生成子目录images和romfs,以及文件autoconfig.h、config.in和两个隐含文件:.config和.depend。 config子目录包含文件及下一级子目录,如 config.in、configure.help、Fixconfig、Makefile、 Mkconfig、Setconfig所有这些文件及子目录 Scripts均与系统配置有关; linux-2.6.x子目录是嵌入式操作系统 uClinux-2.6.x的核心目录,包括下一级子目录 arch、include、init、drivers、fs、ipc、kernel、 lib、Mm、scripts和关键文件Makefile、 rules.make,编译后还要生成新文件romfs.o、linux 和system.map;lib子目录为嵌入式操作系统提供 压缩和改进了的函数库支持;tools子目录包含 romfs-inst.sh文件,通过调用此文件,可以把目录 或文件加入到romfs子目录中;user子目录包含各 种驱动程序文件目录,根据用户的配置情况,不同的 驱动程序会被编译进最后形成的操作系统中; vendors子目录包括与特定硬件平台相关的分类目录 组。目录结构如图1所示。 Makefile的详细介绍情况在 uClinux-dist\linux-2.6.x\Documentation\kbuil d中,如图2所示。图1、目录结构即Linux 内核中的 Makefile 以及与 Makefile 直接相关的文件有:

Makefile 语法分析

Makefile 语法分析第一部分 VERSION = 2 # 给变量VERSION赋值 PATCHLEVEL = 6 # 给变量PATCHLEVEL赋值 SUBLEVEL = 22 # 给变量SUBLEVEL赋值 EXTRAVERSION = .6 # 给变量EXTRAVERSION赋值 NAME = Holy Dancing Manatees, Batman! # 给变量NAME赋值 # *DOCUMENTATION* # To see a list of typical targets execute "make help" # More info can be located in ./README # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. # Do not: # o use make's built-in rules and variables # (this increases performance and avoid hard-to-debug behavour); # o print "Entering directory ..."; MAKEFLAGS += -rR --no-print-directory # 操作符“+=”的作用是给变量(“+=”前面的MAKEFLAGS)追加值。 # 如果变量(“+=”前面的MAKEFLAGS)之前没有定义过,那么,“+=”会自动变成“=”; # 如果前面有变量(“+=”前面的MAKEFLAGS)定义,那么“+=”会继承于前次操作的赋值符;# 如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符 # 在执行make时的命令行选项参数被通过变量“MAKEFLAGS”传递给子目录下的make程序。# 对于这个变量除非使用指示符“unexport”对它们进行声明,它们在整个make的执行过程中始终被自动的传递给所有的子make。 # 还有个特殊变量SHELL与MAKEFLAGS一样,默认情况(没有用“unexport”声明)下在整个make的执行过程中被自动的传递给所有的子make。 # # -rR --no-print-directory # -r disable the built-in impilict rules. # -R disable the built-in variable setttings. # --no-print-directory。 # We are using a recursive build, so we need to do a little thinking # to get the ordering right. # # Most importantly: sub-Makefiles should only ever modify files in # their own directory. If in some directory we have a dependency on # a file in another dir (which doesn't happen often, but it's often # unavoidable when linking the built-in.o targets which finally # turn into vmlinux), we will call a sub make in that other dir, and

AM335x uboot spl分析

AM335x uboot spl分析 芯片到uboot启动流程 ROM → SPL→ uboot.img 简介 在335x 中ROM code是第一级的bootlader。mpu上电后将会自动执行这里的代码,完成部分初始化和引导第二级的bootlader,第二级的bootlader引导第三级bootader,在 ti官方上对于第二级和第三级的bootlader由uboot提供。 SPL To unify all existing implementations for a secondary program loader (SPL) and to allow simply adding of new implementations this generic SPL framework has been created. With this framework almost all source files for a board can be reused. No code duplication or symlinking is necessary anymore. 1> Basic ARM initialization 2> UART console initialization 3> Clocks and DPLL locking (minimal) 4> SDRAM initialization 5> Mux (minimal) 6> BootDevice initialization(based on where we are booting from.MMC1/MMC2/Nand/Onenand) 7> Bootloading real u-boot from the BootDevice and passing control to it. uboot spl源代码分析 一、makefile分析 打开spl文件夹只有一个makefile 可见spl都是复用uboot原先的代码。 主要涉及的代码文件为u-boot-2011.09-psp04.06.00.03/arch/arm/cpu/armv7 u-boot-2011.09-psp04.06.00.03/arch/arm/lib u-boot-2011.09-psp04.06.00.03/drivers LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds 这个为链接脚本 __image_copy_end _end 三、代码解析 __start 为程序开始(arch/arm/cpu/armv7/start.S) .globl _start 这是在定义u-boot的启动定义入口点,汇编程序的缺省入口是 start 标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。

通用Makefile模板及实例

1 通用Makefile——1 实现的功能: ?make——编译和连接程序 ?make objs——编译程序,生成目标文件 ?make clean——清除编译产生的目标文件(*.o)和依赖文件(*.d) ?make cleanall——清除目标文件(*.o)、依赖文件(*.d)和可执行文件(*.exe)?make rebuild——重新编译连接程序,相当于make clean && make Usage: Makefile源代码 # Gneric C/C++ Makefile #################################################### PROGRAM := SRCDIRS := SRCEXTS := CPPFLAGS := CFLAGS := CFLAGS += CXXFLAGS := CXXFLAGS += LDFLAGS := LDFLAGS += SHELL = /bin/sh SOURCES = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS)))) OBJS = $(foreach x,$(SRCEXTS),\ $(patsubst %$(x),%.o,$(filter %$(x),$(SOURCES)))) DEPS = $(patsubst %.o,%.d,$(OBJS)) .PHONY: all objs clean cleanall rebuild all : $(PROGRAM) %.d : %.c @$(CC) -MM -MD $(CFLAGS) {1}lt; %.d : %.C @$(CC) -MM -MD $(CXXFLAGS) {1}lt; objs : $(OBJS) %.o : %.c $(CC) -c $(CPPFLAGS) $(CFLAGS) {1}lt; %.o : %.cpp $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) {1}lt; $(PROGRAM) : $(OBJS) ifeq ($(strip $(SRCEXTS)),.c) $(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS)

uboot版本文件结构

uboot版本文件结构的更新改变 分类:ARM2011-09-22 12:57 339人阅读评论(0) 收藏举报本来是开始分析uboot代码的,但是无论是教材还是网上资料都对于我最新下的uboot原码结构不同,对于还是小白的我不容易找到相应的文件,下面是uboot版本中文件组织结构的改变,,,,, u-boot版本情况 网站:http://ftp.denx.de/pub/u-boot/ 1、版本号变化: 2008年8月及以前 按版本号命名:u-boot-1.3.4.tar.bz2(2008年8月更新) 2008年8月以后均按日期命名。 目前最新版本:u-boot-2011.06.tar.bz2(2011年6月更新) 2、目录结构变化: u-boot目录结构主要经历过2次变化,u-boot版本第一次从u-boot-1.3.2开始发生变化,主要增加了api的内容;变化最大的是第二次,从2010.6版本开始。 u-boot-2010.03及以前版本 ├── api存放uboot提供的接口函数 ├── board根据不同开发板定制的代码,代码也不少 ├── common通用的代码,涵盖各个方面,已命令行处理为主 ├── cpu与体系结构相关的代码,uboot的重头戏 ├── disk磁盘分区相关代码 ├── doc文档,一堆README开头的文件 ├── drivers驱动,很丰富,每种类型的设备驱动占用一个子目录 ├── examples示例程序 ├── fs文件系统,支持嵌入式开发板常见的文件系统 ├── include头文件,已通用的头文件为主 ├── lib_【arch】与体系结构相关的通用库文件 ├── nand_spl NAND存储器相关代码 ├── net网络相关代码,小型的协议栈 ├── onenand_ipl

跟我一起写Makefile(可以注释版)

跟我一起写 Makefile 作者:陈皓 整理:祝冬华

第一部分、概述 (6) 第二部分、关于程序的编译和链接 (6) 第三部分、Makefile 介绍 (7) 一、Makefile的规则 (7) 二、一个示例 (8) 三、make是如何工作的 (9) 四、makefile中使用变量 (10) 五、让make自动推导 (11) 六、另类风格的makefile (12) 七、清空目标文件的规则 (13) 第四部分、Makefile 总述 (13) 一、Makefile里有什么? (13) 1、显式规则。 (14) 2、隐晦规则。 (14) 3、变量的定义。 (14) 4、文件指示。 (14) 5、注释。 (14) 二、Makefile的文件名 (15) 三、引用其它的Makefile (15) 四、环境变量 MAKEFILES (16) 五、make的工作方式 (16) 第五部分、书写规则 (17) 一、规则举例 (17) 二、规则的语法 (17) 三、在规则中使用通配符 (18) 四、文件搜寻 (19) 五、伪目标 (20) 六、多目标 (22) 七、静态模式 (22) 八、自动生成依赖性 (24) 第六部分书写命令 (25) 一、显示命令 (26) 二、命令执行 (26) 三、命令出错 (27) 四、嵌套执行make (28) 五、定义命令包 (30) 第七部分使用变量 (30) 一、变量的基础 (31) 二、变量中的变量 (32) 三、变量高级用法 (34) 四、追加变量值 (37) 五、override 指示符 (37) 六、多行变量 (38)

八、目标变量 (39) 九、模式变量 (40) 第八部分使用条件判断 (40) 一、示例 (40) 二、语法 (42) 第九部分使用函数 (43) 一、函数的调用语法 (44) 二、字符串处理函数 (44) 1、subst (44) 2、patsubst (45) 3、strip (45) 4、findstring (46) 5、filter (46) 6、filter-out (46) 7、sort (47) 8、word (47) 9、wordlist (47) 10、words (47) 11、firstword (48) 12、字符串函数实例 (48) 三、文件名操作函数 (48) 1、dir (48) 2、notdir (48) 3、suffix (49) 4、basename (49) 5、addsuffix (49) 6、addprefix (49) 7、join (50) 四、foreach 函数 (50) 五、if 函数 (50) 六、call函数 (51) 七、origin函数 (51) “undefined” (52) “default” (52) “file” (52) “command line” (52) “override” (52) “automatic” (52) 八、shell函数 (53) 九、控制make的函数 (53) 1、error (53) 2、warning (54) 第十部分 make 的运行 (54)

makefile 中文手册 第六章 _ Makefile中的变量

第六章:Makefile中的变量 在Makefile中,变量是一个名字(像是C语言中的宏),代表一个文本字符串(变量的值)。在Makefile的目标、依赖、命令中引用变量的地方,变量会被它的值所取代(与C语言中宏引用的方式相同,因此其他版本的make也把变量称之为“宏”)。在Makefile中变量有以下几个特征: 1.Makefile中变量和函数的展开(除规则命令行中的变量和函数以外),是在make读取makefile文件时 进行的,这里的变量包括了使用“=”定义和使用指示符“define”定义的。 2.变量可以用来代表一个文件名列表、编译选项列表、程序运行的选项参数列表、搜索源文件的目录列 表、编译输出的目录列表和所有我们能够想到的事物。 3.变量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,尽管在GNU make中没有对变量的命名有其它的限制,但定义一个包含除字母、数字和下划线以外的变量的做法也是不可取的,因为除字母、数字和下划线以外的其它字符可能会在make的后续版本中被赋予特殊含义,并且这样命名的变量对于一些shell来说是不能被作为环境变量来使用的。 4.变量名是大小写敏感的。变量“foo”、“Foo”和“FOO”指的是三个不同的变量。Makefile传统做 法是变量名是全采用大写的方式。推荐的做法是在对于内部定义定义的一般变量(例如:目标文件列表objects)使用小写方式,而对于一些参数列表(例如:编译选项CFLAGS)采用大写方式,但这并不是要求的。但需要强调一点:对于一个工程,所有Makefile中的变量命名应保持一种风格,否则会显得你是一个蹩脚的程序员(就像代码的变量命名风格一样)。 5.另外有一些变量名只包含了一个或者很少的几个特殊的字符(符号)。称它们为自动化变量。 像“$<”、“$@”、“$?”、“$*”等。 6.1变量的引用 当我们定义了一个变量之后,就可以在Makefile的很多地方使用这个变量。变量的引用方式 是:“$(VARIABLE_NAME)”或者“${ VARIABLE_NAME }”来引用一个变量的定义。例如:“$(foo)”或者“${foo}”就是取变量“foo”的值。美元符号“$”在Makefile中有特殊的含义,所有在命令或者文件名中使用“$”时需要用两个美元符号“$$”来表示。对一个变量的引用可以在Makefile的任何上下文中,目标、依赖、命令、绝大多数指示符和新变量的赋值中。这里有一个例子,其中变量保存了所有.o文件的列表: objects = program.o foo.o utils.o program : $(objects) cc -o program $(objects) $(objects) : defs.h 变量引用的展开过程是严格的文本替换过程,就是说变量值的字符串被精确的展开在变量被引用的地方。因此规则: foo = c prog.o : prog.$(foo) $(foo) $(foo) -$(foo) prog.$(foo) 被展开后就是:

uboot_freescale_imx51_start.s_详解

/* * *Purpose: the document is used to learn detailed information aboutimx51 cpu start.S, *referring to some documents on websites. *file address: U-boot-2009.08/Cpu/Arm_cortexa8/start.S * * writer: xfhai 2011.7.22 * *Instruction: *1.@xxxx : indicates annotation *2./***** *** *****/ : stand for code in my files *3.instructions refers to code not included in my file * */ Section 1: uboot overview 大多数bootloader都分为stage1和stage2两部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。 1、Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:==> (1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。 ==>(2)设置异常向量(Exception Vector)。 ==>(3)设置CPU的速度、时钟频率及终端控制寄存器。 ==>(4)初始化内存控制器。 ==>(5)将ROM中的程序复制到RAM中。 ==>(6)初始化堆栈。 ==>(7)转到RAM中执行,该工作可使用指令ldr pc来完成。 2、Stage2 C语言代码部分 lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作: ==>(1)调用一系列的初始化函数。 ==>(2)初始化Flash设备。 ==>(3)初始化系统内存分配函数。 ==>(4)如果目标系统拥有NAND设备,则初始化NAND设备。 ==>(5)如果目标系统有显示设备,则初始化该类设备。 ==>(6)初始化相关网络设备,填写IP、MAC地址等。 ==>(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。

makefile 中 $@ $^ % 使用

makefile 中$@ $^ %< 使用 https://www.360docs.net/doc/c16199397.html,/kesaihao862/article/details/7332528 这篇文章介绍在LINUX下进行C语言编程所需要的基础知识。在这篇文章当中,我们将会学到以下内容:源程序编译Makefile的编写程序库的链接程序的调试头文件和系统求助1.源程序的编译在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器。下面我们以一个实例来说明如何使用gcc编译器。假设我们有下面一个非常简单的源程序(hello.c):int main(int argc,char **argv){printf("Hello Linux\n");}要编译这个程序,我们只要在命令行下执行:gcc -o hello hello.cgcc 编译器就会为我们生成一个hello的可执行文件。执行./hello就可以看到程序的输出结果了。命令行中gcc表示我们是用gcc来编译我们的源程序,-o 选项表示我们要求编译器给我们输出的可执行文件名为hello 而hello.c是我们的源程序文件。gcc编译器有许多选项,一般来说我们只要知道其中的几个就够了。-o 选项我们已经知道了,表示我们要求输出的可执行文件名。-c选项表示我们只要求编译器输出目标代码,而不必要输出可执行文件。-g选项表示我们要求编译器在编译的时候提供我们以后对程序进行调试的信息。知道了这三个选项,我

们就可以编译我们自己所写的简单的源程序了,如果你想要知道更多的选项,可以查看gcc的帮助文档,那里有着许多对其它选项的详细说明。2.Makefile的编写假设我们有下面这样的一个程序,源代码如下:/* main.c */#include "mytool1.h"#include "mytool2.h" int main(int argc,char **argv){mytool1_print("hello");mytool2_print("hello");}/* mytool1.h */ #ifndef _MYTOOL_1_H#define _MYTOOL_1_Hvoid mytool1_print(char *print_str);#endif/* mytool1.c */#include "mytool1.h"void mytool1_print(char *print_str){printf("This is mytool1 print %s\n",print_str);}/* mytool2.h */#ifndef _MYTOOL_2_H#define _MYTOOL_2_Hvoid mytool2_print(char *print_str);#endif/* mytool2.c */#include "mytool2.h"void mytool2_print(char *print_str){printf("This is mytool2 print %s\n",print_str);}当然由于这个程序是很短的我们可以这样来编译gcc -c main.cgcc -c mytool1.cgcc -c mytool2.cgcc -o main main.o mytool1.o mytool2.o这样的话我们也可以产生main程序,而且也不时很麻烦。但是如果我们考虑一下如果有一天我们修改了其中的一个文件(比如说mytool1.c)那么我们难道还要重新输入上面的命令?也许你会说,这个很容易解决啊,我写一个SHELL脚本,让她帮我去完成不就可以了。是的对于这个程序来说,是可

相关文档
最新文档