c, c++用makefile編譯工程



1.、該Makefile 是我自己寫的,可用於編譯均是 .c文件的工程,基本不用修改什麼東西。

2、將該文件拷貝到工程文件夾下,在終端執行:

#make create (將會在根目錄下創建出include, src, bin 三個目錄)

#make 即可編譯

#make clean  刪除所有生成的中間文件和可執行文件

3、如果需要編譯C++.cppC語言的.c文件,即C++混合編譯,將CC=gcc 改爲CC=g++即可。(如果還不行,那就需要在.cpp文件的extern C中將被調用的.c聲明一下,或者將所有.c文件做成成靜態庫,後者適合.c文件較多的情況)

CC=gcc

CFLAGS=-o

PARAM=-Wall

CUR=$(shell pwd)

SRC=$(CUR)/src

SRCS=$(SRC)/*.c

TARGET=$(CUR)/bin/exe

.PHONY:$(TARGET)

all:$(TARGET)

$(TARGET):$(SRCS)

$(CC) $^ $(CFLAGS) $@ $(PARAM)

clean:

@-rm -rf $(TARGET)

@-rm -rf $(CUR)/include/*~

@-rm -rf $(SRC)/*~

@-rm -rf $(CUR)/*~

create:

@-mkdir src/

@-mkdir include/

@-mkdir bin/

----------------------------------------------------------

本文給出萬能Makefile的具體實現,以及對其中的關鍵點進行解析。所謂C++萬能Makefile,即可編譯鏈接所有的C++程序,而只需作很少的修改。

號稱萬能Makefile,一統江湖。我對原版的Makefile做了些修改。首先揭開它的廬山真面目:

####################################################
# Generic makefile - 萬能Makefile
# for compiling and linking C++ projects on Linux 
# Author: George Foot  Modified:Jackie Lee
####################################################
### Customising
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
#
EXECUTABLE := main    # 可執行文件名
LIBDIR:=              # 靜態庫目錄
LIBS :=               # 靜態庫文件名
INCLUDES:=.           # 頭文件目錄
SRCDIR:=              # 除了當前目錄外,其他的源代碼文件目錄
#
# # Now alter any implicit rules' variables if you like, e.g.:

CC:=g++
CFLAGS := -g -Wall -O3
CPPFLAGS := $(CFLAGS)
CPPFLAGS += $(addprefix -I,$(INCLUDES))
CPPFLAGS += -MMD
#
# # The next bit checks to see whether rm is in your djgpp bin
# # directory; if not it uses del instead, but this can cause (harmless)
# # `File not found' error messages. If you are not using DOS at all,
# # set the variable to something which will unquestioningly remove
# # files.
#

RM-F := rm -f


# # You shouldn't need to change anything below this point.
#
SRCS := $(wildcard *.cpp) $(wildcard $(addsuffix /*.cpp, $(SRCDIR)))
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.cpp,$(MISSING_DEPS)))


.PHONY : all deps objs clean veryclean rebuild info

all: $(EXECUTABLE)

deps : $(DEPS)

objs : $(OBJS)

clean :
        @$(RM-F) *.o
        @$(RM-F) *.d
veryclean: clean
        @$(RM-F) $(EXECUTABLE)

rebuild: veryclean all
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
        @$(RM-F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(EXECUTABLE) : $(OBJS)
        $(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -L,$(LIBDIR)) $(addprefix -l,$(LIBS))

info:
        @echo $(SRCS)
        @echo $(OBJS)
        @echo $(DEPS)
        @echo $(MISSING_DEPS)
        @echo $(MISSING_DEPS_SOURCES)

注:1)命令行前的空白符必須爲一個製表符(Tab);如,@$(RM-F) *.o前不是空格,而是一個製表符;

內容解析

1.Makefile基本語法

target爲要生成的目標文件;dependency爲target的依賴文件;command爲用於生成target的命令行;

<target> : <dependency> <dependency> ...
(tab)<command>
(tab)<command>
 .
 .
 .

2.賦值符號 := 與 =

  :=與=的區別在於,符號:=表示立即展開變量值。例如:

A:=foo

B:=$(A)

A:=bar

這時,B的值仍爲foo,因爲它已被展開,不會再隨A的值改變而改變。

3.符號#是Makefile的註釋符號

4.wildcard函數

SRCS:=$(wildcard *.cpp) 表示列舉當前目錄中擴展名爲.cpp的所有文件,然後賦值給變量SRCS。詳細請google之。

5.patsubst函數

OBJS := $(patsubst %.cpp,%.o,$(SRCS))表示,將$(SRCS)中所有滿足模式%.cpp的字符串替換爲%.o。

6.filter-out函數

$(filter-out $(A),$(B))表示從B中過濾掉A中的內容,返回剩餘內容;

7. “.PHONY”

用.PHONY修飾的target是“僞目標”,不需要生成真實的文件;make假定phony target是已經生成的,然後更新它後邊的依賴文件和執行它下邊的命令(command);

8.all deps objs clean veryclean rebuild info

這些都是“僞目標”。

all是第一個目標,所以輸入make時它被默認執行;all生成或更新所有*.cpp文件對應的*.d文件和*.o文件,並鏈接所有*.o文件生成可執行文件$(EXECUTABLE)。

deps僅僅生成*.d文件;.d文件是什麼文件?它包含了代碼文件的依賴信息。

objs僅僅生成*.o文件;.o文件是C++代碼編譯後的中間結果文件,廢話!

clean用於刪除*.d文件和*.o文件。

veryclean刪除*.d文件、*.o文件,還有名爲$(EXECUTABLE)的可執行文件。

rebuild先調用veryclean清除結果文件,再調用all重新編譯和鏈接。

info查看某些信息。

使用方法:

make deps即可執行deps;

9.ifneq...else...endif

條件語句,ifneq表示如果不想等,則...;

10.include <files>語句

include表示把<files>的內容包含進來;

$(DEPS)是包含依賴信息的文件,每個源文件對應一個.d文件;-include $(DEPS)表示把這些依賴信息包含進來;

11.鏈接*.o文件,生成可執行文件

主菜來了!

$(EXECUTABLE) : $(OBJS)
        $(CC) -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))

 

$(EXECUTABLE)爲可執行文件名;$(OBJS)爲所有.o文件名;$(CC)在這裏是g++;$(addprefix -l,$(LIBS)添加引用庫;

前面說好的*.d文件和*.o文件是怎麼生成的呢?貌似沒有命令指出要生成它們呀!請看隱含規則!

12. 隱含規則(Implicit rules)

$(EXECUTABLE)依賴於$(OBJS),但makefile中沒有指明$(OBJS)依賴於誰,也沒指明命令生成它們;

這時,make的隱含規則開始起作用;針對$(OBJS)中的每個目標,make自動調用:

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

依次生成.o文件和.d文件;

$<表示依賴文件列表的第一個文件名;

$@表示目標文件名;

之所以會生成.d文件,是由於“-MMD”這一編譯選項。爲g++加上這一選項後,編譯器會生成文件依賴信息,並存放至.d文件中。

每一個.cpp文件相應地生成一個.d文件和一個.o文件。

13.@符號

命令行前的@符號表示不回顯命令行;

14.CFLAGS和CPPFLAGS

這兩者包含編譯選項,更詳細內容請Google之。

-g 添加gdb調試信息;

-Wall 提示warning信息;

-O3 表示第3級優化;


------------------

基本思路:

最外層有三個文件: Makefile, Make.share, Make.include.

每個工程目錄中都有一個Makefile文件.

其中最外層的Makefile是外殼部分.需要調用者修改裏面的某些參數,例如編譯平臺,要編譯的子目錄集合,每個工程都要用到的頭文件,編譯參數等,設置完成後,會進入每個子目錄,執行當前子目錄中的Makefile文件.

Make.shareMake.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)

鏈接的時候查找順序:

  1. -L 指定的路徑, 從左到右依次查找
  2. 由 環境變量 LIBRARY_PATH 指定的路徑,使用":"分割從左到右依次查找
  3. /etc/ld.so.conf 指定的路徑順序
  4. /lib 和 /usr/lib (64位下是/lib64和/usr/lib64)

動態庫調用的查找順序:

  1. ld的-rpath參數指定的路徑, 這是寫死在代碼中的
  2. ld腳本指定的路徑
  3. LD_LIBRARY_PATH 指定的路徑
  4. /etc/ld.so.conf 指定的路徑
  5. /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是先找到的,並且沒有同名動態庫存在.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章