【项目构建】04:动态库与静态库制作

发布于:2024-05-02 ⋅ 阅读:(23) ⋅ 点赞:(0)

1.编译动态链接库

动态链接库:不会把代码编译到二级制文件中,而是在运行时才去加载,所以只需要维护一个地址即可,

动态库编译完成之后需要发布,否则程序运行时找不到,

windows环境下动态库为.dll、linux环境下动态库为.so

  1. 编译成 .o 文件:g++ -c -fpic soTest.cpp -o soTest.o

  2. 编译动态库:g++ -shared soTest.o -o libsoTest.so

    • -c:得到二进制文件aTest.o
    • -shared:共享
    • -fPIC:产生位置无关的代码,
    • -l:小写l,指定动态库,
    • -L:手动指定库文件搜索目录,默认只链接共享目录,
    • -I:大写i,指定头文件目录(默认当前目录),
  3. 链接成执行文件:

    g++ [.cpp] -l [libName] -L [libPath] -o [test.out]

g++ soTest.cpp -shared -fPIC -o libsoTest.so
(1)编译动态库

文件目录结构如下,将其打包成动态库,

在这里插入图片描述

// soTest.h
#ifndef _SOTEST_H
#define _SOTEST_H

#include <iostream>
using namespace std;

class soTest {
public:
    void func1();
    virtual void func2();
    virtual void func3() = 0;
};

#endif
// soTest.cpp
#include "soTest.h"

void soTest::func1()
{
    cout << "this is func1" << endl;
}

void soTest::func2()
{
    cout << "this is func2" << endl;
}
# makefile
libsoTest.so:
	$(CXX) soTest.cpp -shared -fPIC -L ./ -o libsoTest.so
clean:
	$(RM) libsoTest.so

使用make libsoTest.so 命令成功完成对 libsoTest.so 动态库的打包操作,

(2)链接动态库

在动态库成功打包出来之后,在其他项目中通过引入 soTest.hlibsoTest.so文件,来使用打包好的动态库,

文件目录结构如下,将第三方动态库动态载入,编译自己的项目,

在这里插入图片描述

//test.cpp
#include <iostream>
#include "soTest.h"
using namespace std;

class Test:public soTest{
public:
    void func2() {
        cout << "Test:this is func2" << endl;
    }
    void func3() {
        cout << "Test:this is func3" << endl;
    }
};

int main() {
    Test t1;
    t1.func1();
    t1.func2();
    t1.func3();
    return 0;
}
# makefile
test:
	$(CXX) test.cpp -lsoTest -L ./ -I ./ -o test.out
clean:
	$(RM) *.out

使用make test 命令成功完成第三方动态库的链接,编译成功目录下出现 test.out 的可执行文件,

(3)运行时使用动态库

由于动态库的特点,若只在编译时使用的动态库,而运行时没有指定动态库位置,则程序将无法正常运行,

即动态库编译完成之后需要进行发布操作,否则程序运行时会找不到动态库位置而产生报错,如下所示:

./a.out: error while loading shared libraries: libsoTest.so: cannot open shared object file: No such file or directory
  • 解决方案1:将动态库so文件拷贝到对应的目录下(发布),才能运行程序

    • linux下默认动态库路径配置文件:/etc/ld.so.conf/etc/ld.so.conf.d/*.conf

    • /usr/lib

    • /usr/local/lib

  • 解决方案2:运行时手动指定动态库的所在目录

    mac环境:

    • DYLD_LIBARY_PATH=./your_lib_path
    • export DYLD_LIBARY_PATH

    linux环境:

    • LD_LIBARY_PATH=./your_lib_path
    • export LD_LIBARY_PATH

在这里插入图片描述

2.编译静态链接库

静态链接库:会将库中的代码编译到二进制文件中,当程序编译完成后,该库文件可以删除,

与静态库不同的是,动态链接库必须与程序同时部署,还要保证程序能正常加载得到的库文件。静态库可以不用部署已经加载到程序中,而且运行时的速度更快,

但是会导致程序体积更大,并且库中的内容如果有更新,则需要重新编译生成程序,

windows环境下动态库为.lib、linux环境下动态库为.a

  1. 编译成 .o 文件:g++ -c aTest.cpp -o aTest.o

  2. 编译静态库:ar -r libaTest.a aTest.o

    • -c:得到二进制文件aTest.o
    • ar:备份压缩命令,将目标文件打包成静态链接库,
    • -r:将文件插入备存文件中,
  3. 链接成执行文件:

    g++ [.cpp] [.a] -o [test.out]

    g++ [.cpp] -l [libName] -L [libPath] -o [test.out]

(1)编译静态库

文件目录结构如下,将其打包成静态库,

在这里插入图片描述

// aTest.h
#ifndef _ATEST_H
#define _ATEST_H

#include<iostream>
using namespace std;

class aTest{
public:
	void func1();
};

#endif
// aTest.cpp
#include "aTest.h"

void aTest::func1()
{
	cout << "aTest:func1" << endl;
}
# makefile
libaTest.a:
	$(CXX) -c aTest.cpp -L ./ -I ./ -o aTest.o
	$(AR) -r libaTest.a aTest.o
clean:
	$(RM) *.a *.o

使用make libaTest.a 命令成功完成对 libaTest.a 静态库的打包操作,

(2)链接静态库

在静态库成功打包出来之后,在其他项目中通过引入 aTest.hlibaTest.a文件,来使用打包好的静态库,

文件目录结构如下,将第三方静态库动态载入,编译自己的项目,

在这里插入图片描述

// test.cpp
#include <iostream>
#include "aTest.h"
using namespace std;

int main() {
    aTest t1;
    t1.func1();
    return 0;
}
# makefile
test:
	$(CXX) test.cpp -laTest -L ./ -I ./ -o test.out
clean:
	$(RM) *.out 

使用make test 命令成功完成第三方静态库的链接,编译成功目录下出现 test.out 的可执行文件,

(3)运行时使用静态库

由于静态库的特点,在编译时已经将库中的代码编译到二进制文件中,当编译完成后,该库文件可以删除,并且程序可以直接运行,

在这里插入图片描述

3.make install

  1. make,编译链接:

    将源文件,编译成二进制的可执行文件(包括各种库文件)

  2. make install,配置相关的运行环境:

    创建目录,将可执行文件拷贝到指定目录(安装目录)

    加全局可执行的路径

    加全局的启动停止脚本

  3. make clean

    重置编译环境,删除无关文件

TARGET:=my_test
OBJ:=$(TARGET).os

CC:=g++

PATHS:=/tmp/test/
BIN:=/usr/local/bin/

START_SH:=$(PATHS)$(TARGET)
STOP_SH:=$(PATHS)$(TARGET)

$(TARGET):$(OBJ)

install:$(TARGET)
	if [ -d $(PATHS) ];						\
		then echo $(PATHS) exist;			\
	else 									\
		mkdir $(PATHS);						\
		cp $(TARGET) $(PATHS);				\
		ln -sv $(PATHS)$(TARGET) $(BIN);	\
		touch $(LOG);						\
		chmod a+rwx $(LOG);					\
		echo "$(TARGET)>$(LOG) & echo $(TARGET) running...">$(PATHS)$(START_SH);	\
		echo "killall $(TARGET)">$(PATHS)$(START_SH); 								\
		chmod a+x $(PATHS)$(START_SH);												\
		chmod a+x $(PATHS)$(STOP_SH);												\
		ln -sv $(PATHS)$(START_SH) $(BIN);											\
		ln -sv $(PATHS)$(STOP_SH) $(BIN);											\
	fi;

clean:
	$(RM) $(TARGET) $(OBJ) $(BIN)$(TARGET) $(BIN)$(START_SH) $(BIN)$(STOP_SH)
	$(RM) -rf $(PATHS)

.PHONY:clean install