V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ChristopherWu
V2EX  ›  程序员

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

  •  
  •   ChristopherWu · 2020-07-19 12:53:56 +08:00 · 2241 次点击
    这是一个创建于 1578 天前的主题,其中的信息可能已经有所发展或是发生改变。

    实际上是最近遇到了一个 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 版本的动态链接库,就没这个符号了。

    解决方法

    • 卸载 apt install 的 libgit2,这样子/user/local/lib/pkgconfig就会被卸载
    • 将``sudo cp libgit2.pc /usr/lib/pkgconfig/` 改成拷到优先读目录(不推荐,复写了 apt 安装的版本)

    6 条回复    2020-07-20 11:39:23 +08:00
    msg7086
        1
    msg7086  
       2020-07-19 13:13:10 +08:00   ❤️ 2
    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
        2
    ChristopherWu  
    OP
       2020-07-19 13:34:53 +08:00
    @msg7086 学习了。

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

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

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

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

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

    用发行版最好所有系统软件都用 deb 包,不要手动编译安装。
    只有那种无所谓有无的小程序,编译安装也无伤大雅。
    nightwitch
        4
    nightwitch  
       2020-07-19 14:36:04 +08:00   ❤️ 1
    良好的习惯是 手动编译安装软件,prefix 指定到自己的家目录,并且不要添加到环境变量,链接的时候手动指定目录或者临时 export 环境变量,不要 install 到 /usr/之类的目录去,不然和系统的起冲突是迟早的事。
    chengxiao
        5
    chengxiao  
       2020-07-20 10:14:05 +08:00
    既然知道是环境变量的问题.....那么解决办法不应该是
    export PKG_CONFIG_PATH=/xxx/xxx (.pc 文件目录)么?
    ChristopherWu
        6
    ChristopherWu  
    OP
       2020-07-20 11:39:23 +08:00
    @chengxiao 不行的,你再看看文章里的那块?除非你显示的修改这个环境变量
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5328 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:16 · PVG 15:16 · LAX 23:16 · JFK 02:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.