golang 编译问题让我搞懂 apt install 是怎么运行的

2020-07-19 12:53:56 +08:00
 ChristopherWu

实际上是最近遇到了一个 bug,跟以前写的一篇笔记有相关,就整理一下发出来了。 [toc]

背景

编译一个golang的项目时,提示:缺少git_index_add_from_buffer的动态库。

# github.com/libgit2/git2go/v30
/tmp/go-build349513601/b205/_x019.o: In function `_cgo_eec00726eb8d_Cfunc_git_index_add_from_buffer':
/tmp/go-build/cgo-gcc-prolog:161: undefined reference to `git_index_add_from_buffer'
collect2: error: ld returned 1 exit status
Makefile:21: recipe for target 'binary' failed
make: *** [binary] Error 2

项目时依赖了libgit这个库,搜索git_index_add_from_buffer也可以确认函数是来自libgit

但是我之前因为版本问题,在apt install libgit因为版本不对(最新是 0.08 )后,看到项目 Makefile 里有编译安装的方法,就跑了一下,升级到1.0.1

	cd /tmp
	rm -fr libgit2-1.0.1.tar.gz libgit2-1.0.1
	curl -Lv -O https://github.com/libgit2/libgit2/releases/download/v1.0.1/libgit2-1.0.1.tar.gz
	tar xvfz libgit2-1.0.1.tar.gz
	mkdir -p libgit2-1.0.1/build
	cd libgit2-1.0.1/build
	cmake ..
	cmake --build .
	sudo cp libgit2.pc /usr/lib/pkgconfig/
	sudo cp libgit2.so.1.0.1 /usr/lib
	sudo ln -s /usr/lib/libgit2.so.1.0.1 /usr/lib/libgit2.so
	sudo cp -aR ../include/* /usr/local/include/

解决了问题的了,但为何在链接时还提示缺少git_index_add_from_buffer的引用呢。

调查

root@localhost:/#  readelf -s /usr/lib/libgit2.so.1.0  2>&1 | grep git_index_add_from_buffer
   782: 000000000006d9c0   426 FUNC    GLOBAL DEFAULT   12 git_index_add_from_buffer
  2680: 000000000006d9c0   426 FUNC    GLOBAL DEFAULT   12 git_index_add_from_buffer

可以看到,动态链接库里确实有这两个符号

root@localhost:/# cat /usr/lib/pkgconfig/libgit2.pc
prefix="/home/cloud/go_dir/src/github.com/git2go/dynamic-build/install"
libdir="/home/cloud/go_dir/src/github.com/git2go/dynamic-build/install/lib"
includedir="/home/cloud/go_dir/src/github.com/git2go/dynamic-build/install/include"

Name: libgit2
Description: The git library, take 2
Version: 1.0.0
Libs: -L${libdir} -lgit2
Libs.private: -lrt -lpthread -lssh2
Requires.private: openssl zlib
Cflags: -I${includedir}

pc 文件就是给pkg-config用的 meta 信息。

可以看到,libgit2.pc文件里的 Version 确实是1.0.0

但是pkg-config里记录的 version 确实0.08

root@localhost:/# pkg-config --modversion libgit2
0.08

可见,问题的根源就是这个了,在使用 libgit2 时,pkg-config读取了 0.08 的动态链接库,结果没有git_index_add_from_buffer

pkg-config is a helper tool used when compiling applications and libraries.

It helps you insert the correct compiler options on the command line so an application can use gcc -o test test.cpkg-config --libs --cflags glib-2.0`` for instance, rather than hard-coding values on where to find glib (or other libraries). It is language-agnostic, so it can be used for defining the location of documentation tools, for instance.

必要的背景知识

pkg-config: 是一个软件,跟make一样都是软件。linux 在编译与链接程序时需要用到它来找到必要的动态链接库,例子:

gcc -o test test.c `pkg-config --libs --cflags glib-2.0`

pkg-config --libs --cflags glib-2.0 就是找到所有的 glib 依赖:

$ pkg-config --libs --cflags glib-2.0
-I/usr/local/Cellar/glib/2.64.3/include/glib-2.0 -I/usr/local/Cellar/glib/2.64.3/lib/glib-2.0/include -I/usr/local/opt/gettext/include -I/usr/local/Cellar/pcre/8.44/include -L/usr/local/Cellar/glib/2.64.3/lib -L/usr/local/opt/gettext/lib -lglib-2.0 -lintl

结论

接上, 问题根源就是 golang 编译时,去找libgit2的库时,在pkg-config里,读的 pc 文件不是指向0.08动态链接库的版本库。

找一下pkg-config读 pc 文件的规则,就发现pkg-config找 pc 文件,是根据环境变量来找的:

 pkg-config --variable pc_path pkg-config
/usr/local/lib/x86_64-linux-gnu/pkgconfig:
/usr/local/lib/pkgconfig:
/usr/local/share/pkgconfig:
/usr/lib/x86_64-linux-gnu/pkgconfig:
/usr/lib/pkgconfig:/usr/share/pkgconfig

然后就是从上往下找libgit2的 pc 文件.

Makefile 在拷 libgit2 的配置时,拷到了sudo cp libgit2.pc /usr/lib/pkgconfig/

但是刚好,我之前用 apt install 装过,所以在/user/local/lib/pkgconfig里就有了 libgit2 的配置了。。所以一直用了 28 版本的动态链接库,就没这个符号了。

解决方法

2185 次点击
所在节点    程序员
6 条回复
msg7086
2020-07-19 13:13:10 +08:00
pc 和 apt 没有关系。

apt 的包也是编译并安装,只不过是安装到临时目录中,然后把整个目录打包。
里面会有 pc 文件是因为编译安装时生成了 pc 文件而已。

这里的问题是你没有把软件编译成 deb 包,所以万事都要手动管理,包括卸载软件包,手动复制所有的文件,处理 pc 文件等等。不如修改一下打包脚本,直接打包成 deb 。
https://salsa.debian.org/debian/libgit2

不过说实话,根本问题是你 git2go 用了最新版吧。
https://packages.debian.org/buster-backports/golang-gopkg-libgit2-git2go.v28-dev
git2go v28 本来就在软件源里。
ChristopherWu
2020-07-19 13:34:53 +08:00
@msg7086 学习了。

> 里面会有 pc 文件是因为编译安装时生成了 pc 文件而已。

这个不知道诶,所以所有的包(链接库)都是默认放系统指定目录的?

> 你没有把软件编译成 deb 包,所以万事都要手动管理,包括卸载软件包,手动复制所有的文件,处理 pc 文件等等

太高端了,手动管理不是比较麻烦?

> 根本问题是你 git2go 用了最新版吧
是的,项目里用了最新版。
msg7086
2020-07-19 14:24:09 +08:00
Debian 打包有自己的规范,官方打包是会放在指定目录的。
https://packages.debian.org/buster-backports/amd64/libgit2-dev/filelist
可以看到默认安装在 /usr/lib/x86_64-linux-gnu/libgit2.so 这个位置。

用发行版最好所有系统软件都用 deb 包,不要手动编译安装。
只有那种无所谓有无的小程序,编译安装也无伤大雅。
nightwitch
2020-07-19 14:36:04 +08:00
良好的习惯是 手动编译安装软件,prefix 指定到自己的家目录,并且不要添加到环境变量,链接的时候手动指定目录或者临时 export 环境变量,不要 install 到 /usr/之类的目录去,不然和系统的起冲突是迟早的事。
chengxiao
2020-07-20 10:14:05 +08:00
既然知道是环境变量的问题.....那么解决办法不应该是
export PKG_CONFIG_PATH=/xxx/xxx (.pc 文件目录)么?
ChristopherWu
2020-07-20 11:39:23 +08:00
@chengxiao 不行的,你再看看文章里的那块?除非你显示的修改这个环境变量

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/691313

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX