复杂多目录的Makefile模板及示例 | 迟思堂工作室
A-A+

复杂多目录的Makefile模板及示例

2014-09-03 22:17 GNU/Linux系统 暂无评论 阅读 10,281 次

大约一年多以前,还在移植u-boot,当时参考了u-boot的Makefile,做了一个多目录的Makefile模板。如今,一年过去了,本篇文章还是折腾Makefile模板,本人的水平由此可见一斑。当时的模板没有考虑很多东西,比如将源代码文件及目标文件分离。此次再写一个相对复杂的Makefile模板,自此之后,估计不想再折腾了。

本文的Makefile跟以往的模板一样,都是参考u-boot的做法(u-boot项目无论代码还是文件组织,都有很多值得学习的地方)。由下列文件组成:

1、配置文件config.mk,主要是编译器、编译选项、头文件路径、目标文件、源文件路径,等等;

2、依赖规则rules.mk,主要是产生depend文件,有了depend文件,当修改了头文件后,包含该头文件的源文件都会被重新编译。

3、主Makefile,或称为总Makefile,此文件调用其它目录的Makefile以及上述文件,以产生库及可执行文件。

4、子目录Makefile,各模块的Makefile,主要是生成静态库。

除子目录Makefile在各个模块目录中外,其它文件都在项目的当前目录。

Makefile模板应用场合及特点缺点说明如下:

1、头文件统一在include目录(或其它自定义的目录),子模块分布于各子目录,该目录的Makefile将模块编译成静态库,主代码(包括main函数及其它的代码)文件在独立目录,该目录的Makefile为总Makefile(或称主Makefile)。

2、修改头文件后,所有依赖该文件者均重新编译。

3、可指定编译后的文件所在目录,如不指定,则在原目录下生成目标文件(.o、.out文件)。指定方式如下:

1)、命令行:$ make O=../build

2)、$ export BUILD_DIR=../build ; make

3)、直接在主Makefile指定目录

4、自动识别编译C、C++文件。

下面分别解释一下各个文件

1、配置文件config.mk

模板如下:

#

# (C) Copyleft 2011

# Late Lee from 

# A simple file to specify compier and macros and flags

# and ways to compile .c & .cpp

#

# cross compile...

CROSS_COMPILE =

CC = $(CROSS_COMPILE)gcc

CXX = $(CROSS_COMPILE)g++

AR = $(CROSS_COMPILE)ar

ARFLAGS = cr

RM = -rm -rf

MAKE = make

CFLAGS =  #-Wall

debug = y

ifeq ($(debug), y)

CFLAGS += -g

else

CFLAGS += -O2 -s

endif

DEFS =

CFLAGS += $(DEFS)

LDFLAGS = $(LIBS)

# include path here

INCDIRS = $(SRCTREE)/./include/

CFLAGS += -I$(INCDIRS)

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

ifneq ($(OBJTREE),$(SRCTREE))

ifeq ($(CURDIR),$(SRCTREE))

dir :=

else

dir := $(subst $(SRCTREE)/,,$(CURDIR))

endif

obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)

src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)

$(shell mkdir -p $(obj))

else

obj :=

src :=

endif

suffix = $(notdir $(CURDIR))

export suffix

# test of shell script

MKCONFIG    := $(SRCTREE)/mkconfig

export MKCONFIG

# export to other Makefile

export CC

export CFLAGS

export INCDIRS

export AR

export ARFLAGS

export RM

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

# make all .c or .cpp

$(obj)%.o: %.c

@echo "Compling: " $(addsuffix .c, $(basename $(notdir $@)))

@$(CC) $(CFLAGS) -c $< -o $@

$(obj)%.o: %.cpp

@echo "Compling: " $(addsuffix .cpp, $(basename $(notdir $@)))

@$(CXX) $(CFLAGS) -c $< -o $@

主要分3部分:

第一部分指定编译器及编译选项,可以选择交叉编译器,头文件路径由INCDIRS 宏指定,这里有的编译选项CFLAGS处理得不太好,比如debug版本及release版本是通过“-g”和“-O2 -s”来区别的,但这里是直接指定,并不能在make时指定。

第二部分指定了源代码目录及目标文件目录,这些目录由主Makefile给出,或者在make时给出。

第三部分是编译c或c++源代码的规则。

实际使用中,此文件需要根据实际情况修改编译器及编译选项。

2、依赖规则rules.mk

内容如下:

#

# (C) Copyleft 2011

# Late Lee from 

#

# A simple way to generate depend file(.depend) for .c & .cpp,

# so you change the head file, it will recompile the 

# file(s) which include the head file.

#

_depend: $(obj).depend

$(obj).depend: $(SRCTREE)/config.mk $(SRC_C) $(SRC_CPP)

@rm -f $@

@for f in $(SRC_C)do \

g=`echo $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \

$(CC) -MM $(CFLAGS) -E -MQ $(obj)$$g $$f >> $@ ; \

done

@for f in $(SRC_CPP)do \

g=`echo $$f | sed -e 's/\(.*\)\...\w/\1.o/'`; \

$(CC) -MM $(CFLAGS) -E -MQ $(obj)$$g $$f >> $@ ; \

done

该文件主要是产生.depend依赖文件,依赖文件的作用在前面已经说过,此处不再提及。实际使用中,经文件无需修改。

3、主Makefile

内容如下:

#

# (C) Copyleft 2011

# Late Lee from 

# A simple Makefile for multi-directory

# Most idea comes from U-boot project

#

# object file directory, eg

# $ make O=../build

# $ export BUILD_DIR=../build ; make

# or modify BUILD_DIR here

ifdef O

ifeq ("$(origin O)""command line")

BUILD_DIR := $(O)

endif

endif

ifneq ($(BUILD_DIR),)

saved-output := $(BUILD_DIR)

# Attempt to create a output directory.

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

# Verify if it was successful.

BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)

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

endif  # ifneq ($(BUILD_DIR),)

OBJTREE     := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

SRCTREE     := $(CURDIR)

TOPDIR      := $(SRCTREE)

LNDIR       := $(OBJTREE)

export        TOPDIR SRCTREE OBJTREE

export        TOPDIR SRCTREE OBJTREE

include $(TOPDIR)/config.mk

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

# libs

### your libs here

LIBS :=

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

LDFLAGS = $(LIBS)

# source file(s), including c file(s) cpp file(s)

# you can also use $(wildcard *.c), etc.

### main source here

MAIN_DIR =  #main/

SRC_C   :=

SRC_CPP := $(wildcard $(MAIN_DIR)*.cpp)

$(if $(MAIN_DIR), $(shell mkdir -p $(addprefix $(obj),$(MAIN_DIR))),)

# object file(s)

OBJ_C   := $(addprefix $(obj),$(patsubst %.c,%.o,$(SRC_C)))

OBJ_CPP := $(addprefix $(obj),$(patsubst %.cpp,%.o,$(SRC_CPP)))

# executable file here

target = $(obj)a.out

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

all: config $(target)

config:

@echo "Generating... hello.h"

@$(MKCONFIG)

$(target)$(LIBS) exe

exe: $(OBJ_CPP)

@echo "Generating executable file..." $(notdir $(target))

@$(CXX) $(CFLAGS) $(OBJ_CPP) -o $(target) $(LIBS)

$(LIBS):

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

clean:

@echo "cleaning..."

@$(RM) $(target)

@$(RM) hello.h

@find $(OBJTREE) -type f \

\( -name 'core' -o -name '*.bak' -o -name '*~' \

-o -name '*.o'  -o -name '*.a' -o -name '*.exe' -o -name '.depend' \) -print \

| xargs rm -f

distclean:

@echo "distcleaning..."

@$(RM) $(target)

@$(RM) hello.h

@find $(OBJTREE) -type f \

\( -name 'core' -o -name '*.bak' -o -name '*~' \

-o -name '*.o'  -o -name '*.a' -o -name '*.exe' -o -name '.depend' \) -print \

| xargs rm -f

@$(RM) $(obj)

.PHONY: all clean distclean $(LIBS) exe

该文件也可分为三部分。第一部分指定源代码目录及目标文件目录,第二部分指定了工程所需的静态库以及主代码所在目录,第三部分编译规则,包括make all,make clean,等等命令的规则。实际使用中,需要根据实际情况添加子模块静态库名称、主代码名称。

4、子目录Makefile

内容如下:

#

# (C) Copyleft 2011

# Late Lee from 

# A simple Makefile for lib(libxxx.a)

# You just need to change library name and source file(s)

#

include $(TOPDIR)/config.mk

# here you can set your own flags

CFLAGS +=  # -D_A_

# static library name

LIB =   # $(obj)libfoo.a

# source file(s), including c file(s) cpp file(s)

# you can also use $(wildcard *.c), etc.

SRC_C   :=  # foo.c

SRC_CPP :=

# object file(s)

OBJ_C   := $(addprefix $(obj),$(patsubst %.c,%.o,$(SRC_C)))

OBJ_CPP := $(addprefix $(obj),$(patsubst %.cpp,%.o,$(SRC_CPP)))

all: $(obj).depend $(LIB)

$(LIB)$(OBJ_C) $(OBJ_CPP)

@echo "Generating static library: " $(notdir $@)

@$(AR) $(ARFLAGS) $@ $^

clean:

@echo "Cleaning..."

@$(RM) $(OBJ_C) $(OBJ_CPP) $(LIB) *.bak *~ *.d *.o $(obj).depend

.PHONY: all clean

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

include $(TOPDIR)/rules.mk

sinclude $(obj).depend

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

子目录的Makefile比较简单,因为编译配置及depend文件都在其它文件中,这里直接包含进来即可。

实际使用中,需要修改的地方有:

1、配置文件config.mk,指定头文件路径,编译选项,额外库,等等。

2、主Makefile,指定各子模块的库名称及生成可执行文件名称

3、子Makefile,指定静态库名称即可,每一个模块目录都需要一个Makefile。

文中的Makefile模板,远远不如IDE方便,因此,用与不用,则仁者见仁了。

本文语法着色由迟思堂工作室提供高亮工具手工完成。

2014.04.30补充:

本文所涉及的Makefile模块及示例仓库请参考下面地址:

如果有git客户端,请直接clone出工程:



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate



标签:

给我留言