20、打造专业的编译环境

大型项目的目录结构(无第三方库)

每一个模块下都有一个makefile,各个makefile是关联的关系。项目中的makefile链接各模快。build用来存储编译的结果。每一个模块都定义对应生成.dep和.o程序。app.out是链接所有目标文件生成的,将build下各模块中的.o文件打包成build下的.a文件。只需将build下的.a文件链接起来就得到可执行程序。

项目架构设计分析:

项目被划分为多个不同模块:

每个模块的代码用一个文件夹进行管理:文件夹由inc,src,makefile构成。

每个模块的对外函数声明统一放置于common/inc中:

如:common.h   xxxfunc.h

需要打造的编译环境:

源码文件夹在编译时不能被改动(只读文件夹)。

在编译时自动创建文件夹(build)用于存放编译结果。

编译过程中能够自动生成依赖关系,自动搜索需要的文件。

每个模块可以拥有自己独立的编译方式。

支持调试版本的编译选项。

解决方案设计:

第一阶段:将每个模块中的代码编译成静态库文件。

compile:

project(makefile)   common(makefile) module(makefile) main(makefile)   -->common.a module.a main.a

第二阶段:将每个模块的静态库文件链接成最终可执行程序。

link:

project(makefile)-->common.a madule.a main.a -->app.out

第一阶段任务:

完成可用于各个模块编译的makefile文件。

每个模块的编译结果为静态库文件(.a文件)。

关键的实现要点:

自动生成依赖关系(gcc -MM)

自动搜索需要的文件(vpath)

将目标文件打包为静态库文件(ar crs)

模块makefile中的构成:

module(makefile) -include(deps)【gcc -MM %.c】 sed

                                  -all ->module.a->%.o->gcc %.c

第一阶段makefile

.PHONY : all
DIR_BUILD :=/home/delphi/make/build
DIR_COMMON_INC :=/home/delphi/make/common/inc
DIR_SRC :=src
DIR_INC :=inc
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP :=.dep
AR :=ar 
# dabao minling jingtaiku
ARFLAGS :=crs
CC :=gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC)
ifeq ($(DEBUG),true)
CFLAGS += -g
endif
MODULE :=$(realpath .)
#huoqu xiangdui lujing de juedui lujing
MODULE :=$(notdir $(MODULE))
#取出前缀值  只剩下当前文件夹名字
DIR_OUTPUT :=$(addprefix $(DIR_BUILD)/, $(MODULE))
OUTPUT :=$(MODULE).a
OUTPUT :=$(addprefix $(DIR_BUILD)/, $(OUTPUT))
SRCS :=$(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS :=$(SRCS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS :=$(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS))
DEPS :=$(SRCS:$(TYPE_SRC)=$(TYPE_DEP))
DEPS :=$(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS))
vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPT_INC) $(DIR_COMMON_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)
-include $(DEPS)
all: $(OUTPUT)
@echo "SUCCESS! TARGET==>$(OUTPUT)"
$(OUTPUT) : $(OBJS)
$(AR) $(ARFLAGS) $@ $^
$(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC)
$(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^)

$(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC) 
@echo "creatint $@ ..."
@set -e;\
$(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,\(.*\)\.o[ :]*,$(DIR_OUTPUT)/\1$(TYPE_OBJ) $@ : ,g' > $@

#用sed替换,路径替换到output文件夹下,依赖文件的名字也加到依赖条件中

21、打造编译环境中

第二阶段任务:

完成编译整个工程的makefile文件。

调用模块makefile编译生成静态库文件。

链接所有模块的静态库文件,最终得到可执行程序。

project(makefile)-->common.a mudule.a main.a-->app.out

关键的实现要点:

如何自动创建build文件夹以及子文件夹?

如何进入每一个模块文件夹进行编译?

编译成功后如何链接所有模块静态库?

开发中的经验假设:

项目中的各个模块在设计阶段就已经基本确定,因此,在之后的开发过程中不会频繁随意的增加或减少。

解决方案设计:

1、定义变量保存模块名列表(模块名变量)

2、利用Shell中的for循环遍历模块名变量

3、在for循环中进入模块头文件夹进行编译

4、循环结束后链接所有的模块静态库文件

makefile中嵌入Shell的for循环:

#模块名列表

MODULES :=common\

                      module \

                      main

test :

    @set -e(发生错误后立即退出执行);\

    for dir in $(MODULES); \  #dir是Shell中的变量,循环变量

     do\    #Shell中的for循环

            echo $$dir;  \  (循环体中打印模块名)

    done

注意事项:

makefile中嵌入Shell代码时,如果需要使用Shell变量的值,必须在变量名前加上$$(例:$$dir) !

2工程makefile中的关键构成

project(makefile)->compile(mkdir module cd module make all cd ..)静态库文件

                                ->link (gcc -o app.out*.a)

链接时的注意事项:

gcc在进行静态库链接时必须遵循严格的依赖关系。

gcc -o app.out x.a y.a z.a

其中的依赖关系必须为:x.a->y.a, y.a->z.a

默认情况下遵循自左向右的依赖关系。

如果不清楚库间的依赖,可以使用-Xlinker自动确定依赖关系

gcc -o app.out -Xlinker "-(" z.a y.a x.z -Xlinker "-)"

第二节点makefile:

.PHONY : all compile link clean rebuild
MODULES :=common \
  module \
      main
MKDIR :=mkdir
RM :=rm -fr
CC :=gcc
LFLAGS :=
DIR_PROJECT := $(realpath .)
DIR_BUILD :=build
DIR_BUILD_SUB :=$(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB :=$(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
APP :=app.out
APP :=$(addprefix $(DIR_BUILD)/,$(APP))
all :compile $(APP)
@echo "SUCCESS TARGET==>$(APP)" 
compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
@echo "begin to compile ..."
@set -e; \
for dir in $(MODULES); \
do \
cd $$dir && $(MAKE) all DEBUG:=$(DEBUG) && cd .. ; \
done
@echo "success"
link $(APP): $(MODULE_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"
$(DIR_BUILD) $(DIR_BUILD_SUB) :
$(MKDIR) $@
clean :
@echo "begin to clean"
$(RM) $(DIR_BUILD)
@echo "clean success"

rebuild : clean all

22、编译环境下

问题一所有模块makefile中使用的编译路径均为写死的绝对路径,一旦项目文件夹移动,编译必将失败!

DIR_BUILD:=/home/delphi/make/build

DIR_COMMON_INC :=/home/delphi/make/common/inc

写死的绝对路径导致项目源码位置不能移动。

解决方案:

在工程makefile中获取项目的源码路径。

根据项目源码路径:

拼接得到编译文件夹的路径(DIR_BUILD)

拼接得到全局包含路径(DIR_COMMON_INC)

通过定义命令行变量将路径传递给模块makefile。

.PHONY : all compile link clean rebuild
MODULES :=common \
  module \
      main
MKDIR :=mkdir
RM :=rm -fr
CC :=gcc
LFLAGS :=
DIR_PROJECT := $(realpath .)
DIR_BUILD :=build
DIR_COMMON_INC :=common/inc
DIR_BUILD_SUB :=$(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB :=$(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
APP :=app.out
APP :=$(addprefix $(DIR_BUILD)/,$(APP))
all :compile $(APP)
@echo "SUCCESS TARGET==>$(APP)" 
compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
@echo "begin to compile ..."
@set -e; \
for dir in $(MODULES); \
do \
cd $$dir && \
$(MAKE) all \
        DEBUG:=$(DEBUG) \
        DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \
        DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC))&& \
cd .. ; \
done
@echo "success"
link $(APP): $(MODULE_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"
$(DIR_BUILD) $(DIR_BUILD_SUB) :
$(MKDIR) $@
clean :
@echo "begin to clean"
$(RM) $(DIR_BUILD)
@echo "clean success"
rebuild : clean all

问题二:

所有模块makefile的内容完全相同(复制黏贴)

当模块makefile需要改动时,将涉及多处相同的改动!

解决方案:

将模块makefile拆分成两个模块文件:

mod-cfg.mk:定义可能改变的变量。

mod-rule.mk:定义相对稳定的变量和规则。

默认情况下:

模块makefile复用模板文件实现功能(include)

关键问题:

模块makefile如何知道模板文件的具体位置?

解决方案:

通过命令行变量进行模板文件位置的传递。

模块makefile:

include $(MOD_CFG)
#.PHONY : all
#DIR_BUILD :=/home/delphi/make/build
#DIR_COMMON_INC :=/home/delphi/make/common/inc
#  custmization begin
#
#DIR_SRC :=src
#DIR_INC :=inc
#TYPE_INC := .h
#TYPE_SRC := .c
#TYPE_OBJ := .o
#TYPE_DEP :=.dep
#
#custimization end 特殊配置
AR :=ar 
# dabao minling jingtaiku
#为什么没删除?模块配置文件相当与模块makefile的一个全局配置,也是一种默认配置,有可能源文件和头文件直接放到模块文件夹下边,这时需要局部特殊配置,所以这上边可能要特殊的配置了,如
#DIR_SRC := .
#DIR_INC := . 直接在目录中
ARFLAGS :=crs
CC :=gcc
CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC)
ifeq ($(DEBUG),true)
CFLAGS += -g
endif

include $(MOD_RULE)

mod-cfg.mk:

#DIR_BUILD :=/home/delphi/make/build
#DIR_COMMON_INC :=/home/delphi/make/common/inc
DIR_SRC :=src
DIR_INC :=inc
TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o
TYPE_DEP :=.dep

全局makefile:

.PHONY : all compile link clean rebuild
MODULES :=common \
  module \
      main
MKDIR :=mkdir
RM :=rm -fr
CC :=gcc
LFLAGS :=
DIR_PROJECT := $(realpath .)
DIR_BUILD :=build
DIR_COMMON_INC :=common/inc
DIR_BUILD_SUB :=$(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB :=$(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
MOD_CFG :=mod-cfg.mk
MOD_RULE :=mod-rule.mk
APP :=app.out
APP :=$(addprefix $(DIR_BUILD)/,$(APP))
all :compile $(APP)
@echo "SUCCESS TARGET==>$(APP)" 
compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
@echo "begin to compile ..."
@set -e; \
for dir in $(MODULES); \
do \
cd $$dir && \
$(MAKE) all \
        DEBUG:=$(DEBUG) \
        DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \
        DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \
        MOD_CFG:=$(addprefix $(DIR_PROJECT)/,$(MOD_CFG)) \
        MOD_RULE:=$(addprefix $(DIR_PROJECT)/,$(MOD_RULE)) && \
cd .. ; \
done
@echo "success"
link $(APP): $(MODULE_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"
$(DIR_BUILD) $(DIR_BUILD_SUB) :
$(MKDIR) $@
clean :
@echo "begin to clean"
$(RM) $(DIR_BUILD)
@echo "clean success"

rebuild : clean all

3工程makefile(总的)的重构:

拆分命令变量,项目变量,以及其他变量和规则到不同文件

cmd-cfg.mk: 定义命令相关的变量。

pro-cfg.mk:  定义项目变量以及编译路径变量等。

pro-rule.mk: 定义其它变量和规则。

最后的工程makefile通过包含拆分后的文件构成(include)

cmd-cfg.mk: 

AR :=ar
ARFLAGS :=crs
CC :=gcc
LFLAGS:=
CFLAGS :=-I$(DIR_INC) -I$(DIR_COMMON_INC)
ifeq ($(DEBUG),true)
CFLAGS +=-g
endif
MKDIR :=mkdir
RM :=rm -fr

pro-cfg.mk: 

MODULES :=common \
  module \
      main
MOD_CFG :=mod-cfg.mk
MOD_RULE :=mod-rule.mk
CMD_CFG :=cmd-cfg.mk
DIR_BUILD :=build
DIR_COMMON_INC :=common/inc
APP :=app.out

pro-rule.mk: 

.PHONY : all compile link clean rebuild
DIR_PROJECT := $(realpath .)
DIR_BUILD_SUB :=$(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB :=$(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
APP :=$(addprefix $(DIR_BUILD)/,$(APP))
all :compile $(APP)
@echo "SUCCESS TARGET==>$(APP)" 
compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
@echo "begin to compile ..."
@set -e; \
for dir in $(MODULES); \
do \
cd $$dir && \
$(MAKE) all \
        DEBUG:=$(DEBUG) \
        DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \
        DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \
        CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \
        MOD_CFG:=$(addprefix $(DIR_PROJECT)/,$(MOD_CFG)) \
        MOD_RULE:=$(addprefix $(DIR_PROJECT)/,$(MOD_RULE)) && \
cd .. ; \
done
@echo "success"
link $(APP): $(MODULE_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"
$(DIR_BUILD) $(DIR_BUILD_SUB) :
$(MKDIR) $@
clean :
@echo "begin to clean"
$(RM) $(DIR_BUILD)
@echo "clean success"

rebuild : clean all

工程makefile:

include pro-cfg.mk
include cmd-cfg.mk
include pro-rule.mk

模块makefile:

include $(MOD_CFG)

include $(CMD_CFG)
include $(MOD_RULE)

小结:大型项目的编译环境是由不同makefile构成的。

编译环境的设计需要依据项目的整体架构设计。

整个项目的编译过程可以分解为不同阶段。

根据不同的阶段有针对性的对makefile进行设计。

makefile也需要考虑复用性和维护性等基本程序特性。

23、模块的独立编译

一般而言,不同工程师负责不同模块的开发,编译环境中如何支持模块的独立编译?

问题背景:

大型项目的代码成千上万,完整编译的时间长。

编写模块代码时,可通过编译检查语法错误。

为了提高开发效率,需要支持指定模块的独立编译。

示例:

>>make main

Begin to compile main

。。。

success=>/homo/delphi/make/build/main.a

解决方案:

将模块名(module)作为目标名(伪目标)简历规则

目标(module)对应的依赖为build build/module 模块文件夹

规则中的命令进入对应的模块文件夹进行编译。

编译结果存放于build文件夹下。

关键技术点:

如何获取make命令行中指定编译的模块名?

预定义变量:$(MAKECMDGOALS):命令行中指定的目标名(make的命令行参数)

$(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS) 是否可以写成$(DIR_BUILD)/$@ ?

# 不行,只创建了build,make不会认为$@是目标的名字,不会把他替换为目标的名字

自动化变量只能用于规则的命令中,不能用于规则的依赖中。

cd $@ &&\

$(MAKE) all \

....

$(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS)
@echo "begin to compile $@"
@set -e; \
cd $@ && \
$(MAKE) all \
        DEBUG:=$(DEBUG) \
        DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \
        DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \
        CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \
        MOD_CFG:=$(addprefix $(DIR_PROJECT)/,$(MOD_CFG)) \
        MOD_RULE:=$(addprefix $(DIR_PROJECT)/,$(MOD_RULE)) && \
cd .. ; \

makefile中的代码复用:

当不同规则中的命令大量重复时,可考虑自定义函数。

makefile中的自定义函数是代码复用的一种方式。

define func

    @echo "my name is $(0)." 当前函数名字

    @echo "param==>$(1)"  当前函数第一个参数

endef

==>

rule1 :

    $(call func, p1)

rule2:

    $(call func, p2) p2作为参数调用func

思路:

将编译模块的命令集作为自定义函数的具体实现。

函数参数为模块名,函数调用后编译参数指定的模块、

在不同的规则中调该函数。

define makemodule

        cd $(1) $$\

        $(MAKE) all\

              ...\

        cd ..

endef

pro-rule.mk 改

.PHONY : all compile link clean rebuild $(MODULES)
DIR_PROJECT := $(realpath .)
DIR_BUILD_SUB :=$(addprefix $(DIR_BUILD)/, $(MODULES))
MODULE_LIB := $(addsuffix .a, $(MODULES))
MODULE_LIB :=$(addprefix $(DIR_BUILD)/, $(MODULE_LIB))
APP :=$(addprefix $(DIR_BUILD)/,$(APP))
define makemodule
cd ${1} && \  #使用第一个参数作为需要编译的模块名字
$(MAKE) all \
    DEBUG:=$(DEBUG) \
    DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) \
    DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) \
    CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) \
    MOD_CFG:=$(addprefix $(DIR_PROJECT)/,$(MOD_CFG)) \
    MOD_RULE:=$(addprefix $(DIR_PROJECT)/,$(MOD_RULE)) && \
cd .. ; 
endef
all :compile $(APP)
@echo "SUCCESS TARGET==>$(APP)" 
compile : $(DIR_BUILD) $(DIR_BUILD_SUB)
@echo "begin to compile ..."
@set -e; \
for dir in $(MODULES); \
do \
$(call makemodule,$$dir)\
done
@echo "success"
link $(APP): $(MODULE_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"
$(DIR_BUILD) $(DIR_BUILD_SUB) :
$(MKDIR) $@
clean :
@echo "begin to clean"
$(RM) $(DIR_BUILD)
@echo "clean success"
rebuild : clean all
$(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS)
@echo "begin to compile $@"
@set -e; \

$(call makemodule, $@)

小结:

编写模块代码时可通过模块独立编译快速检查语法错误。

自动变量只能在规则的命令中使用,不能在依赖中使用。

makefile中的自定义函数是代码复用的一种方式。

当不同规则中的命令大量重复时,可考虑自定义函数。

24、第三方库的使用支持

问题:当需要使用第三方库时,如何修改?

经验假设:

第三方库通过函数调用的方式提供库中的功能。

库文件发布时都附带了声明库函数原型的头文件。

编译阶段使用头文件,链接阶段使用库文件。

第三方库在项目中的位置:

projext下的libs:inc(dlib.h slib.h) lib(第三方库文件)(dib.so(动态链接库,对应dlib.h)  slib.a(静态链接库),对应slib.h)

common libs都提供子功能,地位是一样的。

第三方库的编译阶段支持:

定义变量DIR_LIBS_INC 用于指示头文件的存储位置。

DIR_LIB_INC :=$(DIR_PROJECT)/libs/inc

使用DIR_LIBS_INC提示make头文件的存储位置。

vpath %$(TYPE_INC) $(DIR_LIBS_INC) 编译子模块时到哪里找头文件

使用DIR_LIBS_INC提示编译器头文件的存储位置。

DFLAGS +=-I$(DIR_LIBS_INC)  编译时找头文件

改:

 DIR_LIBS_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_LIBS_INC)) \

DIR_LIBS_INC :=libs/inc

vpath %$(TYPT_INC) $(DIR_LIBS_INC)

CFLAGS :=-I$(DIR_INC) -I$(DIR_COMMON_INC) -I$(DIR_LIBS_INC)

注意事项:

定义DIR_LIBS_LIB :=libs/lib (第三方库所在路径)。

链接时不会直接链接DIR_LIBS_LIB中的库文件。

需要先将库文件拷贝到DIR_BUILD文件夹。

必须考虑拷贝后的库文件和原始库文件的新旧关系。

$(DIR_BUILD)/% : $(DIR_LIBS_LIB)/%

        $(CP) $^ $@ (拷贝)

第三方库的链接阶段支持:

定义变量EXTERNAL_LIB用于保存第三方库列表。

目标link需要依赖于第三方库列表。

link $(APP) : $(MODULE_LIB)  $(EXTERNAL_LIB) 必须作为最后一个依赖出现?(处理极端情况,如果第三方库文件名与我们的子模块名字一样,优先考虑子模块编译得到的库文件)

    @echo "begin to link .."

    $(CC) $(LFLAGS) -o $(APP) -Xlinker  "-(" $^ -Llinker "-)"

    @echo "Link success"

cmd-cfg.mk:

CP := cp

pro-cfg.mk:

DIR_LIBS_LIB :=libs/lib

pro-rule.mk:

EXTERNAL_LIB :=$(wildcard $(DIR_LIBS_LIB)/*)

EXTERNAL_LIB :=$(patsubst $(DIR_LIBS_LIB)/%,$(DIR_BUILD)/%,$(EXTERNAL_LIB))

link $(APP): $(MODULE_LIB) $(EXTERNAL_LIB)
@echo "begin to link"
$(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS)
@echo "link success"

添加新功能验证旧功能:

回归测试: 在添加新功能时,新功能功能正常,而且验证已经有的功能是否也任然工作正常。

小结:

编译环境必须支持第三方库的使用(静态库或动态库)

工程开发中一般会使用特殊的文件夹存放第三方库。

第三方库所附带的头文件用于声明库函数(编译阶段需要)

在链接阶段先将库文件拷贝到build文件夹,在进行链接。

猜你喜欢

转载自blog.csdn.net/ws857707645/article/details/80805566