您的位置:首页 > 编程语言 > C语言/C++

c, c++用makefile编译工程

2012-04-28 07:50 399 查看
基本思路:

最外层有三个文件: Makefile,
Make.share, Make.include.

每个工程目录中都有一个Makefile文件.

其中最外层的Makefile是外壳部分.需要调用者修改里面的某些参数,例如编译平台,要编译的子目录集合,每个工程都要用到的头文件,编译参数等,设置完成后,会进入每个子目录,执行当前子目录中的Makefile文件.

Make.share和Make.include是供每个工程目中Makefile来包含的,里面包含标准Makefile机制所需要文件的代码.

工程目录下的Makefile : 设置每个工程自己的项目类型(可执行程序,静态库,动态库)和工程模块的名字,自己独特的头文件目录,编译、链接参数.在文件的开头包含最外层的Makefile.include,在文件结尾包含最外层的Makefile.share.

最外层的Makefile文件 :

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

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

# The target platform information.

# You can change the MACRO 'PLATFORM' or use make : make PLATFORM=[IOS, LINUX, MACOS]

# LINUX MACOS IOS

PLATFORM= LINUX

export PLATFORM

# SUB DIRS

SUBDIRS:= \

./hello/hellostaticlib \

./hello/hellodynamiclib \

./hello

# ROOT DIR

ROOTDIR:=$(shell
pwd)

export ROOTDIR

# Global includes

GLOBAL_INCLUDES:= -I$(ROOTDIR)/hello/include

export GLOBAL_INCLUDES

# Global flags for compile

GLOBAL_CCXXFLAGS:= -g -DUNICODE -D_UNICODE

export GLOBAL_CCXXFLAGS

# Target path

TARGET_PATH:=$(ROOTDIR)/target

export TARGET_PATH

# IOS SDK Version

IOSSDK_V=
5.0

export IOSSDK_V

#IOS CPU

IOS_ARCH= armv6 armv7 i386

export IOS_ARCH

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

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

SUPPORTCMDS:= all objs clean cleanall rebuild

export SUPPORTCMDS

.PHONY: SUPPORTCMDS ioscombine

$(SUPPORTCMDS):

ifeq ($(PLATFORM),
IOS)

for subdir in$(SUBDIRS);
do \

echo "Making " $$subdir; \

for arch in$(IOS_ARCH);
do \

echo "For " $$arch; \

(cd $$subdir && make clean && make $@ ARCH=$$arch ); \

done; \

done;

else

for subdir in$(SUBDIRS);
do \

echo "Making " $$subdir; \

(cd $$subdir && make $@); \

done;

endif

TS=$(notdir
$(wildcard $(TARGET_PATH)/$(firstword $(IOS_ARCH))/*))

TSA=$(foreach
arch, $(IOS_ARCH), $(foreach ts, $(TS), $(TARGET_PATH)/$(arch)/$(ts)))

ioscombine:

for ts in$(TS);
do \

echo "Make " $$ts; \

echo$(TSA)
| tr ' ' '\n'| grep $$ts | tr '\n' ' ' | xargs echo; \

echo$(TSA)
| tr ' ' '\n'| grep $$ts | tr '\n' ' ' | xargs lipo -create -output$(TARGET_PATH)/$$ts;
\

done;

最外层的Makefile文件需要修改的有这几个地方:

PLATFORM : 设置目标的平台. LINUX 代表通用linux系统; MACOS 代表mac系统; IOS 代表IPhone,IPad等IOS设备.

SUBDIRS : 设置工程包含的子模块,每个子模块目录下都有自己的Makefile文件.

GLOBAL_INCLUDES : 项目中各模块都会用到的头文件.

GLOBAL_CCXXFLAGS : 项目中各个模块都会用到的编译连接标志.

TARGET_PATH : 如果有必要,可以修改目标binarary的输出路径.

IOSSDK_V : 如果是IOS系统,需要设置编译IOS程序需要的SDK版本号.

IOS_ARCH : 如果是IOS系统,需要设置编译IOS程序需要支持的CPU类型.最后要调用build ioscombine来生成最终目标文件.

最外层的Make.include文件:

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

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

ifneq ($(ARCH),
"")

TARGET_PATH_R:= $(TARGET_PATH)/$(ARCH)

endif

最外层的Make.include文件只是修正TARGET_PATH(即目标路径),如果设置了ARCH(CPU类型),依次把不同类型CPU的输入文件放入各自对应的目录中.只是对IOS类型有效.因此暂时IOS才会用到多CPU支持.

最外层的Make.share文件:

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

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

# The source file types (headers excluded).

# At least one type should be specified.

# The valid suffixes are among of .c, .C, .cc, .cpp, .CPP, .c++, .cp, or .cxx.

SRCEXTS:=.c .cpp

# DONT Modify.

SRCDIR:=./

# The include dirs.

INCLUDES_ALL :=$(GLOBAL_INCLUDES)

INCLUDES_ALL +=$(INCLUDES)

# The flags used for c and c++.

# Wall -Werror # show all warnings and take them as errors

# -fvisibility=hidden # hide the so's export functions

CCXXFLAGS_ALL:=$(GLOBAL_CCXXFLAGS)

CCXXFLAGS_ALL +=$(CCXXFLAGS)

# The compiling flags used only for C.

# If it is a C++ program, no need to set these flags.

# If it is a C and C++ merging program, set these flags for the C parts.

CFLAGS_ALL :=

CFLAGS_ALL +=$(CFLAGS)

# The compiling flags used only for C++.

# If it is a C program, no need to set these flags.

# If it is a C and C++ merging program, set these flags for the C++ parts.

CXXFLAGS_ALL :=

CXXFLAGS_ALL +=$(CXXFLAGS)

# The library and the link options ( C and C++ common).

OFLAGS_ALL :=

OFLAGS_ALL +=$(OFLAGS)

# The EXT-OBJS that include to the target.

EXOBJS_ALL :=

EXOBJS_ALL +=$(EXOBJS)

# The target file name

TARGET:=$(TARGETNAME).exe

ifeq ($(TYPE),
staticlib)

TARGET:= lib$(TARGETNAME).a

endif

ifeq ($(TYPE),
dynamiclib)

TARGET := lib$(TARGETNAME).dll

OFLAGS_ALL +=
-shared -fPIC

endif

TARGET:= $(TARGET_PATH_R)/$(TARGET)

# Modify for different paltform.

## linux

ifeq ($(PLATFORM),
LINUX)

CCXXFLAGS_ALL +=
-DLINUX

endif

## mac os

ifeq ($(PLATFORM),
MACOS)

CCXXFLAGS_ALL +=
-DMACOS

RM= rm

endif

## ios

ifeq ($(PLATFORM),
IOS)

CCXXFLAGS_ALL +=
-DIOS

RM= rm

ifeq ($(ARCH),
i386)

DEVROOT=/Developer/Platforms/iPhoneSimulator.platform/Developer

SDKROOT=$(DEVROOT)/SDKs/iPhoneSimulator$(IOSSDK_V).sdk

else

DEVROOT=/Developer/Platforms/iPhoneOS.platform/Developer

SDKROOT=$(DEVROOT)/SDKs/iPhoneOS$(IOSSDK_V).sdk

endif

CC=$(DEVROOT)/usr/bin/gcc
-arch$(ARCH)

CXX=$(DEVROOT)/usr/bin/g++
-arch$(ARCH)

CCXXFLAGS_ALL+=
-isysroot $(SDKROOT)

OFLAGS_ALL +=
-isysroot$(SDKROOT)

endif

# Stable Section: usually don't need to be changed. But you can add more.

#=============================================================================

SHELL = /bin/sh

SOURCES1=$(foreach
d, $(SRCDIR),$(wildcard $(addprefix $(d)*,$(SRCEXTS))))

SOURCES =$(filter-out
$(EXCLUDESRCS), $(SOURCES1))

OBJS =$(foreach
x,$(SRCEXTS), $(patsubst %$(x),%.o,$(filter %$(x),$(SOURCES))))

DEPS =$(patsubst
%.o,%.d,$(OBJS))

.PHONY: SUPPORTCMDS

all:$(TARGET)

# Rules for creating the dependency files (.d).

#---------------------------------------------------

%.d: %.c

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CFLAGS_ALL)
$<

%.d: %.C

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.cc

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.cpp

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.CPP

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.c++

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.cp

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.d: %.cxx

@$(CC)
-MM -MD$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

# Rules for producing the objects.

#---------------------------------------------------

objs:$(OBJS)

%.o: %.c

$(CC)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CFLAGS_ALL)
$<

%.o: %.C

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.cc

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.cpp

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.CPP

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.c++

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.cp

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

%.o: %.cxx

$(CXX)
-c$(INCLUDES_ALL)$(CCXXFLAGS_ALL)$(CXXFLAGS_ALL)
$<

# Rules for producing the executable.

#----------------------------------------------

$(TARGET):$(OBJS)

echo$(TARGET_PATH_R)

-mkdir -p$(TARGET_PATH_R)

ifeq ($(TYPE),
staticlib)

for a in$(EXOBJS_ALL);
do \

$(AR)
-x $$a; \

done;

ls *.o | xargs$(AR)$(ARFLAGS)$(TARGET)

else

ifeq ($(strip $(SRCEXTS)),
.c) # C file

$(CC) -o$(TARGET)$(OBJS)$(EXOBJS_ALL)$(OFLAGS_ALL)

else # C++ file

$(CXX)
-o$(TARGET)$(OBJS)$(EXOBJS_ALL)$(OFLAGS_ALL)

endif

endif

-include$(DEPS)

rebuild:
clean all

clean:

-$(RM)
*.o *.d

cleanall:
clean

-@$(RM)$(TARGET)

-@$(RM)
"__.SYMDEF" "__.SYMDEF SORTED"

-@$(RM)
*.so *.a

-@$(RM)
-r$(TARGET_PATH)

最外层的Make.share文件是项目中每个模块文件夹下的Makefile应用文件的主体,一般不需要修改.

SRCEXTS : 要编译的文件的扩展名集合.

SRCDIR : 一定不要修改此处,原因不解释.

TARGET : 代码中会根据不同的类型进行选择.可以修改,但是因为可执行程序链接库文件时候如果用-l参数会查找默认名字为lib<>.<>的文件,所以如果目标为库,就表改了.

如果目标为应用程序,则扩展名为exe.按正常来说,非windows系统上应该没有扩展名才合乎逻辑,但是这样做起码没明显害处(O(∩_∩)O~).

如果目标为静态库,则文件格式为lib<modulename>.a

如果目标为动态库,则文件格式为lib<modulename>.so.这里有个问题:在cygwin环境中,如果动态库扩展名为so,会导致链接此so时候找不到文件,而在真实系统中则没有此问题.但是如果扩展名改为dll则无此问题.

DEVROOT, SDKROOT : 是2011年做项目时候的当时MAC机器上的手持设备路径.如果新的系统路径修改了,自己改过来就是了.

这里还涉及到3个定义:LINUX, MACOS和IOS.在工程的代码中可以根据这三个定义的存在与否判断当前编译的环境.也许,是必须根据这三个定义来判断.否则,...

项目中模块下的Makefile文件,标准样式为

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

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

include$(ROOTDIR)/Make.include

TYPE:= exe

TARGETNAME:= test

INCLUDES= -I./ -I./hellostaticlib/ -I./hellodynamiclib/

CCXXFLAGS= -DDEBUG -g

OFLAGS= -L$(TARGET_PATH_R)
-lhellod -lhellos

#INCLUDES =

#CCXXFLAGS =

#CFLAGS =

#CXXFLAGS =

#OFLAGS =

#EXOBJS =

#EXCLUDESRCS =

include$(ROOTDIR)/Make.share

其中两个include所在的行不要修改.

TYPE和TARGETNAME是必选的.

TYPE : 目标模块的类型,可以为为exe, staticlib, dynamiclib.

TARGETNAME : 目标模块的名字.

其他选项为可选项

INCLUDES : 此模块额外需要的头文件目录,格式为 -I<path1> -I<path2> ...

CCXXFLAGS : 此模块中c,c+源代码额外需要的编译选项,例如 -DDEBUG -g.

CFLAGS : 同CCXXFLAGS,只对c代码有效.

CXXFLAGS : 同CCXXFLAGS,只对c++代码有效.

OFLAGS : 此模块额外需要的链接选项,如果要链接其他的库,就在这里设置.

EXOBJS : 如果要添加额外的中间文件(*.o),可以在此添加.

EXCLUDESRCS : 此模块目录中不会被编译链接的文件,以空格分隔.

额外补充

1). 查看依赖

ldd file : 可以查依赖关系.但是在cygwin中如果动态库位so,貌似会出错,如果扩展名为dll则没问题.

2).链接动态库有几种方式

在cygwin里,如果动态库扩展名为so,就要链接时候直接添加文件:<path>\<name>.so了.如果扩展名为dll,则可以用-l来链接.

在真实系统中,如果用-l链接动态库,会涉及到运行时候可能找不到so的问题,有几种解决方案.

a) 链接时候用 -Wl,-rpath,<path of so>. 另外据说-rpath可以用-R代替.这样程序运行时候就会在-rpath指定的路径中找so文件,找不到会报错.

b)拷贝so到/lib或者/usr/lib目录下(/usr/local/lib).这需要管理员权限.

c)将so所在路径追加到/etc/ld.so.conf文件中,然后运行sudo ldconfig命令.这也需要管理员权限.

d)执行程序前,修改LD_LIBRARY_PATH环境变量.在linux终端,可以输入:export LD_LIBRARY_PATH=<path of your so file>: $LD_LIBRARY_PATH:

然后再执行可执行程序.可以用echo $LD_LIBRARY_PATH 来查看设置的环境变量.此方法关闭shell失效.

3).gcc,g++参数解释

-fPIC :表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

4)用-l链接库

编译动态库可以像编译静态库那样采用-Lpath -lxx的方式进行, 但这里存在一个问题,如果在path目录下既有动态库又有静态库的时候, 链接器优先选择采用动态库的方式进行编译.比如在同一目录下存在 libx.a 和 libx.so, 那么在链接的时候会优先选择libx.so进行链接. 这也是为什么在com组维护的第三方库(third, third-64)中绝大多数库的产出物中只有.a的存在, 主要就是为了避免在默认情况下使用到.so的库, 导致在上线的时候出现麻烦(特别是一些系统中存在,但又与我们需要使用的版本有出入的库).

链接器中提供了-dn -dy 参数来控制使用的是动态库还是静态库,-dn表示后面使用的是静态库,-dy表示使用的是动态库

例:

g++ -Lpath -Wl,-dn -lx -Wl,-dy -lpthread

这样如果在path路径下有libx.so和libx.a这个时候只会用到 libx.a.

注意在最后的地方如果没有-Wl,-dy 让后面的库都使用动态库,可能会报出 "cannot find -lgcc_s" 的错误,这是由于glibc的.a库和.so库名字不同,--static会自动处理,但是 -Wl,-dy却不会去识别这个问题.

5)

链接的时候查找顺序:

-L 指定的路径, 从左到右依次查找
由 环境变量 LIBRARY_PATH 指定的路径,使用":"分割从左到右依次查找
/etc/ld.so.conf 指定的路径顺序
/lib 和 /usr/lib (64位下是/lib64和/usr/lib64)

动态库调用的查找顺序:

ld的-rpath参数指定的路径, 这是写死在代码中的
ld脚本指定的路径
LD_LIBRARY_PATH 指定的路径
/etc/ld.so.conf 指定的路径
/lib和/usr/lib(64位下是/lib64和/usr/lib64)

一般情况链接的时候我们采用-L的方式指定查找路径, 调用动态链接库的时候采用LD_LIBRARY_PATH的方式指定链接路径.

另外注意一个问题,就是只要查找到第一个就会返回,后面的不会再查找. 比如-L./A -L./B -lx 在A中有libx.a B中有libx.a和libx.so, 这个时候会使用在./A的libx.a 而不会遵循动态库优先的原则,因为./A是先找到的,并且没有同名动态库存在.

SOURCODE: http://download.csdn.net/detail/patdz/4262746
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: