【Linux】如何将自定义源文件打包并生成静态库

本文详细介绍了如何自定义创建静态库,包括源文件的书写、生成可执行程序、静态库的打包和使用。讨论了在没有头文件和库文件的情况下,编译器预处理失败的问题,以及解决方法。此外,还讲解了静态库的生成过程,如使用ar工具创建.a文件,并通过Makefile自动化构建。最后,文章探讨了如何将库安装到本地,以及在不同情况下动态链接和静态链接的选择。
摘要由CSDN通过智能技术生成

自定义实现一个静态库

源文件的书写

创建以下四个文件,分别是AddSub函数的声明头文件以及它们的定义源文件

my_add.h
#pragma once
#include <stdio.h>
extern int Add(int a, int b);

my_sub.h
#pragma once
#include <stdio.h>
extern int Sub(int a, int b);

my_add.c
#include "my_add.h"
int Add(int a, int b)
{
    printf("enter Add func:%d + %d\n",a,b);
    return a+b;
}

my_sub.c
#include "my_sub.h"
int Sub(int a, int b)
{
    printf("enter Sub func:%d - %d\n",a,b);
    return a-b;
}

按照我们之前的使用方法是:
 创建一个main.c文件去调用它们,比如:

#include "my_add.h"
#include "my_sub.h"

int main()
{
    int a=10;
    int b=20;

    int tmp=Add(a,b);
    printf("result:%d\n",tmp);
    tmp=Sub(a,b);
    printf("result:%d\n",tmp);
    return 0;
}

1.生成可执行程序:

[AMY@VM-12-15-centos lesson_13]$ gcc -o myexe main.c my_add.c my_sub.c

运行结果:
在这里插入图片描述

2.我们还有一种方法,先使用gcc -c(汇编),使其变成可重定位目标二进制文件,后缀是.o

[AMY@VM-12-15-centos lesson_13]$ gcc -c main.c my_add.c my_sub.c
[AMY@VM-12-15-centos lesson_13]$ ll
total 32
-rw-rw-r-- 1 AMY AMY  201 Jun 25 20:53 main.c
-rw-rw-r-- 1 AMY AMY 1736 Jun 25 21:02 main.o
-rw-rw-r-- 1 AMY AMY  110 Jun 25 20:55 my_add.c
-rw-rw-r-- 1 AMY AMY   64 Jun 25 20:47 my_add.h
-rw-rw-r-- 1 AMY AMY 1536 Jun 25 21:02 my_add.o
-rw-rw-r-- 1 AMY AMY  110 Jun 25 20:55 my_sub.c
-rw-rw-r-- 1 AMY AMY   64 Jun 25 20:49 my_sub.h
-rw-rw-r-- 1 AMY AMY 1544 Jun 25 21:02 my_sub.o

然后我们将这些.o文件链接起来,其实跟上面第一种也是同样的道理

[AMY@VM-12-15-centos lesson_13]$ gcc -o myexe main.o my_add.o my_sub.o

这样也同样可以生成可执行文件myexe,到目前为止你可能觉得多次一举,请你接下来慢慢看!

关于库的知识引入

在这里插入图片描述
现在左边的人(目录)需要使用右边的人(目录)提供的库中的代码,但是右边的人,不想要将它的源文件给左边的人,反正.o文件下一步就是链接,所以它将这些.c源文件先预处理编译汇编后的.o文件给左边的人

在这里插入图片描述
但是我们现在对main.c处理,会出现下面报错:

[AMY@VM-12-15-centos test]$ gcc -c main.c
main.c:1:20: fatal error: my_add.h: No such file or directory
 #include "my_add.h"
                    ^
compilation terminated.

原因是:编译源文件第一步就是预处理将头文件展开,main.c使用了原来库的方法,但是它没有头文件所以预处理就失败,所以我们还需要为库的使用者提供.h头文件

[AMY@VM-12-15-centos lesson_13]$ cp *.h ../test

这样我们的使用者就可以执行代码了:

[AMY@VM-12-15-centos test]$ gcc -c main.c
[AMY@VM-12-15-centos test]$ gcc -o myexe main.o my_add.o my_sub.o
[AMY@VM-12-15-centos test]$ ./myexe
enter Add func:10 + 20 = 
result:30
enter Sub func:10 - 20 = 
result:-10

如果我们不想给对方提供我们的源代码,我们可以提供.o可重定位二进制文件,让使用者直接用它的代码链接即可。

我们可以给对方提供.o(方法的实现) + .h(有什么方法)。基于这样我们可以试着将所有的.o文件打一个包,给使用者提供一个库文件即可。
本质上说,打包就是将多个.o,形成一个文件,将所有的方法都放在这个文件里,这个文件就叫做,实际过程中根据打包方式的不同就有了动态库与静态库

库的本质就是.o文件的集合


静态库打包

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库

Linux上的库文件:库的命名规则:libxxx(库名) + .类型后缀.a/.so,静态库后缀.a,动态库后缀.so

生成静态库
	ar -rc libmymath.a add.o sub.o 
ar(archive)是gnu归档工具,rc表示(replace and create)

[AMY@VM-12-15-centos lesson_13]$ file libmymath.a 
libmymath.a: current ar archive(当前ar存档)

我们创建Makefile文件用来编译:

libmymath.a:my_add.o my_sub.o
	ar -rc $@ $^
my_add.o:my_add.c
	gcc -c my_add.c -o my_add.o
my_sub.o:my_sub.c
	gcc -c my_sub.c -o my_sub.o

.PHONY:clean
clean:
	rm -rf *.o libmymath.a

libmymath.a就是我们形成的库文件,但是我们现在就能直接使用吗?答案是不能!!

我们之所以能够在Linux之中写C语言代码,就是因为Linux系统提供了我们编写代码所需的头文件和库文件:

[AMY@VM-12-15-centos lesson_13]$ ls /usr/include/stdio.h
/usr/include/stdio.h
[AMY@VM-12-15-centos lesson_13]$ ls /lib64/libc.a
/lib64/libc.a

将一个库交付给使用者,就是要将库文件.a/.so + 匹配的头文件一起交付

所以我们还需要增加一点命令到Makefile中:

libmymath.a:my_add.o my_sub.o
	ar -rc $@ $^
my_add.o:my_add.c
	gcc -c my_add.c -o my_add.o
my_sub.o:my_sub.c
	gcc -c my_sub.c -o my_sub.o

.PHONY:output
output:
	mkdir -p mylib/include
	mkdir -p mylib/lib
	cp -f *.a mylib/lib
	cp -f *.h mylib/include

.PHONY:clean
clean:
	rm -rf *.o libmymath.a mylib

测试流程:

[AMY@VM-12-15-centos lesson_13]$ make
gcc -c my_add.c -o my_add.o
gcc -c my_sub.c -o my_sub.o
ar -rc libmymath.a my_add.o my_sub.o
[AMY@VM-12-15-centos lesson_13]$ ls
libmymath.a  Makefile  my_add.c  my_add.h  my_add.o  my_sub.c  my_sub.h  my_sub.o
[AMY@VM-12-15-centos lesson_13]$ make output
mkdir -p mylib/include
mkdir -p mylib/lib
cp -f *.a mylib/lib
cp -f *.h mylib/include
[AMY@VM-12-15-centos lesson_13]$ ls
libmymath.a  Makefile  my_add.c  my_add.h  my_add.o  mylib  my_sub.c  my_sub.h  my_sub.o
[AMY@VM-12-15-centos lesson_13]$ tree mylib
mylib
├── include
│   ├── my_add.h
│   └── my_sub.h
└── lib
    └── libmymath.a

2 directories, 3 files

到了这里,我们就相当于把我们的库发布出来,如果我们想让被人使用还需要打包(成为压缩文件):

[AMY@VM-12-15-centos lesson_13]$ tar czf mylib.tgz mylib

在这里插入图片描述
使用者将包文件解压:

[AMY@VM-12-15-centos test]$ tar xzf mylib.tgz
[AMY@VM-12-15-centos test]$ ll
total 12
-rw-rw-r-- 1 AMY AMY  201 Jun 25 21:18 main.c
drwxrwxr-x 4 AMY AMY 4096 Jun 25 22:19 mylib
-rw-rw-r-- 1 AMY AMY  965 Jun 25 22:23 mylib.tgz
[AMY@VM-12-15-centos test]$ tree mylib
mylib
├── include
│   ├── my_add.h
│   └── my_sub.h
└── lib
    └── libmymath.a

2 directories, 3 files

在这里插入图片描述
安装的本质就是拷贝,就是将这些头文件和库文件拷贝到系统指定存放(头文件库文件)且能找到的地方


作为使用者如何使用静态库

让我们一步一步,发现错误并解决错误

当前目录文件:

[AMY@VM-12-15-centos test]$ ll
total 8
-rw-rw-r-- 1 AMY AMY  201 Jun 25 21:18 main.c
drwxrwxr-x 4 AMY AMY 4096 Jun 25 22:19 mylib

我们直接开始使用gcc编译:

[AMY@VM-12-15-centos test]$ gcc -o mymath main.c
main.c:1:20: fatal error: my_add.h: No such file or directory
 #include "my_add.h"
                    ^
compilation terminated.

gcc是编译器,编译器在搜索文件时,先搜索当前路径,再去系统默认指定路径去搜索

错误:找不到头文件也找不到库文件,虽然说这些头文件以及库文件都在mylib路径下,但是跟main.c并不是处于同级目录下,编译器无法找到,并且再系统指定路径也没有找到,所以这里出现错误

为了解决上述编译器搜索不到的问题,我们可以给gcc指定搜索路径:

[AMY@VM-12-15-centos test]$ gcc -o mymath main.c -I ./mylib/include
/tmp/cciXM8Xc.o: In function `main':
main.c:(.text+0x21): undefined reference to `Add'
main.c:(.text+0x47): undefined reference to `Sub'
collect2: error: ld returned 1 exit status

错误:我们已经指定搜索路径了,这里报错跟上面已经不一样了,现在的错误是很明显是链接错误,到了链接错误,说明我们已经完成了预处理编译汇编的任务,离成功已经不远了。现在的问题就是头文件已经找到了,但是库文件还找不到

我们同样可以指定库文件搜索路径:(我们可以理解-I就是include首字母大写、-L就是lib首字母大写)

[AMY@VM-12-15-centos test]$ gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib
/tmp/ccHHUV6L.o: In function `main':
main.c:(.text+0x21): undefined reference to `Add'
main.c:(.text+0x47): undefined reference to `Sub'
collect2: error: ld returned 1 exit status

错误:虽然我们指定了库文件的搜索路径,但是还是报了编译错误,这又是为什么呢??

原因:如果要链接第三方的库,我们必须要指明库名称,也就是如果我们要链接myliblib中的libmymath.a库文件,我们必须要写明这个库的名称,也就是mymath(库的名称我们需要去掉前缀lib去掉后缀.a/.so

为什么我们使用别的语言比如C、C++不需要指定库名称呢?因为它们默认就指定了使用什么库,gcc、g++也会默认识别自带的库。而我们使用第三方库,编译器不知道你到底要执行什么库,而且当前目录也可能存在多个库文件,有太多不确定因素

最终的正确编译指令:-l是指定库名

gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib -l mymath

在这里插入图片描述

关于生成静态库相关问题

截至上一步我们已经将静态库生成好,并且使用者使用这个静态库且生成了可执行程序,我们现在使用lddfile命令来查看这个可执行程序的基本信息:

[AMY@VM-12-15-centos test]$ ldd mymath
	linux-vdso.so.1 =>  (0x00007ffed21b1000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fda8fa78000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fda8fe46000)
[AMY@VM-12-15-centos test]$ file mymath
mymath: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, 
BuildID[sha1]=fdb301e54e2ada49fe9c3ea53d59a8632188c59e, not stripped

问题:我们使用ldd并没有发现我们自己写的库,而且我们发现我们生成的静态库生成的可执行程序链接的却都是.so后缀的动态库,我们使用file命令也发现mymathdynamically linked动态链接

gcc默认是动态链接,对于一个特定的库,到底是动态链接还是静态链接取决于你提供的是动态库还是静态库,但如果动静态库都提供给你了,默认是链接动态库。而且,一个可执行程序,可能不止链接一个库,只要链接了动态库,那就是动态链接。而在这里虽然我们只提供了静态库,但是gcc没有办法,只能将这个库拷贝来使用。

另外一种使用静态库的方法(安装库到本地)

下面的这种行为就是安装:

[AMY@VM-12-15-centos test]$ tree mylib
mylib
├── include
│   ├── my_add.h
│   └── my_sub.h
└── lib
    └── libmymath.a

2 directories, 3 files
[AMY@VM-12-15-centos test]$ sudo cp mylib/include/* /usr/include/
[AMY@VM-12-15-centos test]$ sudo cp mylib/lib/*.a /lib64/

当然,我们还是需要指定库名称,因为这是我们自己写的第三方库,而这个目录下的库文件太多,gcc不能自己识别:

[AMY@VM-12-15-centos test]$ gcc main.c -l mymath

运行结果:
在这里插入图片描述

没有经过测试发布的库,不建议直接拷贝到系统库目录下

卸载操作:

[AMY@VM-12-15-centos test]$ sudo rm /usr/include/my_*
[AMY@VM-12-15-centos test]$ sudo rm /lib64/libmymath.a

如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

侠客cheems

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值