GNU Make manual 学习笔记 C语言 / c++ 构建工具
6.静态库,动态库
库命名规范
gcc下:
静态库 libxxxx.a
动态库 libxxxx.so 或者加上版本号 libxxxx.so.major.minor
window下:
静态库 xxx.lib
动态库 xxx.dll
静态库的编译方式
编译为二进制文件 gcc -c xxx.c -o xxx.o
打包 ar rcs libxxx.a xxx.o
进行链接 gcc main.o xxx.o -o xxx.exe(exe是window下才需要的后缀,这样写是为了好看。)
动态库的编译方式
编译为二进制文件 gcc -c xxx.c -o xxx.o
打成动态库 gcc -fPIC -shared xxx. -o libxxx.so
进行链接 gcc -L . main.o libxxx.so -o xxx.exe
6-1.静态库
最简单的理解方式是,将.c的源文件,编译为.o文件,然后使用ar命令,将同一个lib的.o文件打包在一起。
6-1-1.头文件和源文件,在同一个目录
plus.c
int plus(int first,int second)
{
return first+second;
}
plus.h
#ifndef plus
#define plus
int plus_method(int ,int );
#endif
invoke_plus.c
#include "plus.h"
#include <stdio.h>
int invoke_plus()
{
int result=plus(2,3);
printf("result=%d",result);
}
invoke_plus.h
#ifndef invoke_plush
#define invoke_plush
void invoke_plus_print();
#endif
main.c
#include "invoke_plus.h"
int main()
{
invoke_plus();
}
上面的文件都在同一级目录
Makefile
cc=gcc #指定编译器
main_source=main.o #会放在依赖,写成.o触发pattern rule,自动将.c文件编译为.o文件
lib_source =plus.o invoke_plus.o
lib=libarch.a #指定静态库的名字
.PHONY: clean all
all:main.exe $(lib)
$(lib): $(lib_source)
ar -rcs $@ $^ #将静态库的.o文件进行打包
rm -rf $^ #清除没用的.o文件
main.exe:$(main_source) $(lib)
gcc -o $@ $< $(lib) #链接.o文件
clean:
rm -rf *.o *.exe
6-1-2.头文件和源文件,不在同一个目录
文件的内容不变,仅改变文件的路径
.c-->.o的时候需要指定include的路径
CFLAGS=-I static_lib_header
配置Makefile
.PHONY: all clean print
#定义lib的.O文件,会用在prerequisites ,触发pattern rule,从.c文件编译为.o文件
lib_source =static_lib_src/plus.o static_lib_src/invoke_plus.o
main_source=src/main.o
#使用pattern rule 编译的时候,由于lib_source的源文件和头文件分开,需要指定include的路径
CFLAGS=-I static_lib_header
#定义最终生成的exe文件的保存路径
main_dest=build/Debug/
#定义lib生成的路径,就是静态库的保存路径
lib_dest=lib/
#定义静态库的名字
static_arch_lib=libStaticArch.a
all:$(static_arch_lib) main.exe
#将pattern rule编译好的.o文件,进行打包
$(static_arch_lib): $(lib_source)
ar -rcs $(lib_dest)/$@ $?
rm -rf $?
#将pattern rule生成的main.o文件 和 静态库 进行链接
main.exe:$(main_source) $(static_arch_lib)
gcc $< $(lib_dest)$(static_arch_lib) -o $(main_dest)$@
#pattern rule
# %.o:%.c
# $(COMPILE.c) $(OUTPUT_OPTION) $<
#用在pattern rule的默认变量
# COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
# OUTPUT_OPTION = -o $@
clean:
rm -rf *.o src/*.o lib/*.o *.exe src/*.exe lib/*.exe build/Debug/*.exe
6-1-3.静态库 依赖 静态库
need_plus依赖plus
need_plus.c
#include "plus.h"
#include <stdio.h>
void need_plus_print()
{
int result=plus(2,3);
printf("print in nees_plus :result = %d",result);
}
need_plus.h
#ifndef need_plus
#define need_plus
void need_plus_print();
#endif
main.c
#include "invoke_plus.h"
#include "need_plus.h"
int main()
{
invoke_plus_print();
need_plus_print();
}
Makefile
.PHONY: all clean print
#指定依赖的文件及路径
main_source=src/main.o
lib_source =static_lib_src/plus.o static_lib_src/invoke_plus.o
another_lib_source=another_static_lib_src/need_plus.o
#gcc中每一个include的路径都需要写一个 -I ,在.c文件编译为.o文件时候需要
CFLAGS=-I another_static_header -I static_lib_header
#生成文件的路径
main_dest=build/Debug/
lib_dest=lib/
#库的名字
static_arch_lib=libStaticArch.a
another_arch_lib=libAnother.a
all:$(static_arch_lib) $(another_arch_lib) main.exe
#将每个静态库使用一个target进行打包
$(static_arch_lib): $(lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
$(another_arch_lib):$(another_lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
#打包成exe
main.exe:$(main_source) $(static_arch_lib) $(another_arch_lib)
gcc $< $(lib_dest)/$(static_arch_lib) $(lib_dest)/$(another_arch_lib) -o $(main_dest)$@
rm -rf $<
#COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
#OUTPUT_OPTION = -o $@
%.o:%.c
$(COMPILE.c) $< $(OUTPUT_OPTION)
clean:
rm -rf *.o src/*.o another_static_lib_src/*.o lib/* *.exe src/*.exe lib/*.exe build/Debug/*.exe
生成的结构
6-2动态库
需要使用gcc将.0文件打包,然后链接
gcc -c xxx.c -o xxx.o
打成动态库
gcc -fPIC -shared xxx.o -o libxxx.so
进行链接
gcc -L . main.o libxxx.so -o xxx.exe
6-2-1.头文件和源文件,在同级目录
文件结构
main.c
#include "invoke_sub.h"
int main()
{
sub_method();
}
subtraction.c
int sub(int first,int second)
{
return first-second;
}
subtraction.h
#ifndef subtraction
#define subtraction
int sub(int,int);
#endif
invoke_sub.c
#include "subtraction.h"
#include <stdio.h>
void sub_method()
{
printf("resulte = %d",sub(9,3));
}
invoke_sub.h
#ifndef invoke_sub
#define invoke_sub
void sub_method();
#endif
Makefile
.PHONY:clean all
#动态库的依赖,写成.o会触发pattern rule
shared_lib_source=subtraction.o invoke_sub.o
# main
main_source=main.o
#定义lib的名字
shared_lib=libshared_lib.so
all:$(shared_lib) main.exe
#编译动态库文件
$(shared_lib):$(shared_lib_source)
gcc -fPIC -shared $? -o $@
rm -rf $^
#使用动态库文件和main.o进行链接
main.exe:$(main_source) $(shared_lib)
gcc -L . $(shared_lib) $< -o $@ #-L 表示添加lib的加载路径
rm -rf $<
#gcc -shared -fPIC -o libmyhello.so hello.o
#gcc -o hello main.c -L. -lmyhello
clean:
rm -rf *.o *.exe
6-2-2.头文件和源文件,不在同一目录
在.c-->.o的时候需要include路径
CFLAGS=-I another_static_header -I static_lib_header -I share_lib_header
文件结构
还是上面的源文件,仅仅改一下文件的位置,并且跟原来的文件一起编译
Makefile
.PHONY: all clean print
#定义编译器变量
cc=gcc #编译器
CFLAGS=-I another_static_header -I static_lib_header -I share_lib_header #包含路径,就是include的查找路径,用在.c->.o
#定义prerequires的文件
#main
main_source=src/main.o
#静态库
lib_source =static_lib_src/plus.o static_lib_src/invoke_plus.o
another_lib_source=another_static_lib_src/need_plus.o
#动态库
shared_lib_source=share_lib_src/subtraction.o share_lib_src/invoke_sub.o
#定义库名
#静态库
static_arch_lib=libStaticArch.a
another_arch_lib=libAnother.a
#动态库
shared_lib=libshared_lib.so
#定义编译后文件的路径
main_dest=build/Debug/
lib_dest=lib/
# 写rule
all:$(static_arch_lib) $(another_arch_lib) $(shared_lib) main.exe
#静态库
$(static_arch_lib): $(lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
$(another_arch_lib):$(another_lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
#动态库
$(shared_lib):$(shared_lib_source)
gcc -fPIC -shared $? -o $(lib_dest)$@
rm -rf $^
#将全部文件链接为可执行文件
main.exe:$(main_source) $(static_arch_lib) $(another_arch_lib) $(shared_lib)
gcc $< $(addprefix $(lib_dest),$(filter-out $<,$?)) -o $(addprefix $(main_dest),$@)
cp lib/$(shared_lib) build/Debug/$(shared_lib)
rm -rf $<
#由于lib有多个,使用函数$(addprefix)将lib的名字和保存路径和在一起,因为第一个prerequires是main.o使用函数$(filterout)去除,
#由于动态库在程序运行的时候需要被加载,
#要么放到系统指定路径,
#要么在环境变量LD_LIBRARY_PATH添加路径(linux下修改/etc/ld.so.conf),
#要么放在exe的同级目录,这里选择的cp到同级目录
#COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
#OUTPUT_OPTION = -o $@
%.o:%.c
$(COMPILE.c) $< $(OUTPUT_OPTION)
#就是默认pattern rule,将.c编译为.o
clean:
rm -rf *.o src/*.o another_static_lib_src/*.o lib/* *.exe src/*.exe lib/*.exe build/Debug/*
6-2-3.动态库 依赖 动态库
需要将依赖的动态库先编译好,然后写在参数上。
gcc -fPIC -shared $? needed_shared_lib -o $(lib_dest)$@
need_share_lib.c
#include "subtraction.h"
#include <stdio.h>
void needSub()
{
printf("print in need_share_lib: result = %d \n",sub(9,6));
}
need_share_lib.h
#ifndef need_shared_lib
#define need_shared_lib
void needSub();
#endif
main.c
#include "invoke_plus.h"
#include "need_plus.h"
#include "invoke_sub.h"
#include "need_share_lib.h"
int main()
{
invoke_plus_print();
need_plus_print();
sub_method();
needSub();
}
Makefile
.PHONY: all clean print
cc=gcc
CFLAGS=-I another_static_header -I static_lib_header -I share_lib_header -I shareLib_need_sharedLib_header
main_source=src/main.o
lib_source =static_lib_src/plus.o static_lib_src/invoke_plus.o
another_lib_source=another_static_lib_src/need_plus.o
shared_lib_source=share_lib_src/subtraction.o share_lib_src/invoke_sub.o
need_share_lib_source=shareLib_need_shareLib_src/need_share_lib.o #新的动态库的依赖
static_arch_lib=libStaticArch.a
another_arch_lib=libAnother.a
shared_lib=libshared_lib.so
need_share_lib=libneed_share_lib.so #定义新的动态库的名字
main_dest=build/Debug/
lib_dest=lib/
all:$(static_arch_lib) $(another_arch_lib) $(shared_lib) $(need_share_lib) main.exe
$(static_arch_lib): $(lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
$(another_arch_lib):$(another_lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
$(shared_lib):$(shared_lib_source)
gcc -fPIC -shared $? -o $(lib_dest)$@
rm -rf $^
#编译新的动态库,需要添加依赖的动态库进行编译$(lib_dest)$(shared_lib)
$(need_share_lib):$(need_share_lib_source)
gcc -fPIC -shared $? $(lib_dest)$(shared_lib) -o $(lib_dest)$@
rm -rf $^
main.exe:$(main_source) $(static_arch_lib) $(another_arch_lib) $(shared_lib) $(need_share_lib)
gcc $< $(addprefix $(lib_dest),$(filter-out $<,$?)) -o $(addprefix $(main_dest),$@)
cp lib/$(shared_lib) build/Debug/$(shared_lib)
cp lib/$(need_share_lib) build/Debug/
rm -rf $<
#COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
#OUTPUT_OPTION = -o $@
%.o:%.c
$(COMPILE.c) $< $(OUTPUT_OPTION)
clean:
rm -rf *.o src/*.o another_static_lib_src/*.o lib/* *.exe src/*.exe lib/*.exe build/Debug/*
生成的结构
6-3.静态库和动态库相互依赖
6-3-1.静态库依赖动态库
仅仅修改一下上面的代码
invoke_plus.c
#include "plus.h"
#include "subtraction.h"
#include <stdio.h>
void invoke_plus_print()
{
int result=plus(2,3);
printf("print in invoke_plus : result = %d",result);
}
void invoke_sub_dll()
{
printf("print in invoke_plus,invoke shared lib , result = %d\n",sub(8,1));
}
invoke_plus.h
#ifndef invoke_plush
#define invoke_plush
void invoke_plus_print();
void invoke_sub_dll();
#endif
main.c
#include "invoke_plus.h"
int main()
{
invoke_sub_dll();
}
Makefile可以使用上面的,
结构也是一样的。
6-3-2.动态库依赖静态库
在上面的基础上修改一下代码
由于是动态库依赖静态库,需要将静态库的路径写到编译参数
gcc -fPIC -shared $? needed_static_lib -o $(lib_dest)$@
invoke_sub.c
#include "subtraction.h"
#include <stdio.h>
#include "plus.h"
void sub_method()
{
printf("resulte = %d\n",sub(9,3));
}
//动态库中调用静态库的方法plus_method
void share_lib_invoke_static_lib()
{
printf("in share lib invoke static lib , result = %d \n",plus_method(1,2));
}
invoke_sub.h
#ifndef invoke_sub
#define invoke_sub
void sub_method();
void share_lib_invoke_static_lib();
#endif
Makefile,需要在动态库编译的时候,添加静态库的依赖
.PHONY: all clean print
cc=gcc
CFLAGS=-I another_static_header -I static_lib_header -I share_lib_header -I shareLib_need_sharedLib_header
main_source=src/main.o
lib_source =static_lib_src/plus.o static_lib_src/invoke_plus.o
another_lib_source=another_static_lib_src/need_plus.o
shared_lib_source=share_lib_src/subtraction.o share_lib_src/invoke_sub.o
need_share_lib_source=shareLib_need_shareLib_src/need_share_lib.o
static_arch_lib=libStaticArch.a
another_arch_lib=libAnother.a
shared_lib=libshared_lib.so
need_share_lib=libneed_share_lib.so
main_dest=build/Debug/
lib_dest=lib/
all:$(static_arch_lib) $(another_arch_lib) $(shared_lib) $(need_share_lib) main.exe
$(static_arch_lib): $(lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
$(another_arch_lib):$(another_lib_source)
ar -rcs $(lib_dest)$@ $?
rm -rf $?
#由于这个动态库调用了另一个静态库,需要将静态库生成后的库路径添加进来 $(lib_dest)$(static_arch_lib)
$(shared_lib):$(shared_lib_source)
gcc -fPIC -shared $? $(lib_dest)$(static_arch_lib) -o $(lib_dest)$@
rm -rf $^
$(need_share_lib):$(need_share_lib_source)
gcc -fPIC -shared $? $(lib_dest)$(shared_lib) -o $(lib_dest)$@
rm -rf $^
main.exe:$(main_source) $(static_arch_lib) $(another_arch_lib) $(shared_lib) $(need_share_lib)
gcc $< $(addprefix $(lib_dest),$(filter-out $<,$?)) -o $(addprefix $(main_dest),$@)
cp lib/$(shared_lib) build/Debug/$(shared_lib)
cp lib/$(need_share_lib) build/Debug/
rm -rf $<
#COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
#OUTPUT_OPTION = -o $@
%.o:%.c
$(COMPILE.c) $< $(OUTPUT_OPTION)
clean:
rm -rf *.o src/*.o another_static_lib_src/*.o lib/* *.exe src/*.exe lib/*.exe build/Debug/*
目录结构没有改变