cgo编译和链接参数(代码片段)

binhome binhome     2022-12-14     493

关键词:

CGO 编译和链接参数

编译和链接参数是每一个C/C++程序员需要经常面对的问题。构建每一个C/C++应用均需要经过编译和链接两个步骤,CGO也是如此。 本节我们将简要讨论CGO中经常用到的编译和链接参数的用法。

编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

编译参数主要是头文件的检索路径,预定义的宏等参数。理论上来说C和C++是完全独立的两个编程语言,它们可以有着自己独立的编译参数。 但是因为C++语言对C语言做了深度兼容,甚至可以将C++理解为C语言的超集,因此C和C++语言之间又会共享很多编译参数。 因此CGO提供了CFLAGS/CPPFLAGS/CXXFLAGS三种参数,其中CFLAGS对应C语言编译参数(以.c后缀名)、 CPPFLAGS对应C/C++ 代码编译参数(.c,.cc,.cpp,.cxx)、CXXFLAGS对应纯C++编译参数(.cc,.cpp,*.cxx)。

链接参数:LDFLAGS

链接参数主要包含要链接库的检索目录和要链接库的名字。因为历史遗留问题,链接库不支持相对路径,我们必须为链接库指定绝对路径。 cgo 中的 $SRCDIR 为当前目录的绝对路径。经过编译后的C和C++目标文件格式是一样的,因此LDFLAGS对应C/C++共同的链接参数。

pkg-config

为不同C/C++库提供编译和链接参数是一项非常繁琐的工作,因此cgo提供了对应pkg-config工具的支持。 我们可以通过#cgo pkg-config xxx命令来生成xxx库需要的编译和链接参数,其底层通过调用 pkg-config xxx --cflags生成编译参数,通过pkg-config xxx --libs命令生成链接参数。 需要注意的是pkg-config工具生成的编译和链接参数是C/C++公用的,无法做更细的区分。

pkg-config工具虽然方便,但是有很多非标准的C/C++库并没有实现对其支持。 这时候我们可以手工为pkg-config工具创建对应库的编译和链接参数实现支持。

比如有一个名为xxx的C/C++库,我们可以手工创建/usr/local/lib/pkgconfig/xxx.bc文件:

Name: xxx
Cflags:-I/usr/local/include
Libs:-L/usr/local/lib –lxxx2

其中Name是库的名字,CflagsLibs行分别对应xxx使用库需要的编译和链接参数。如果bc文件在其它目录, 可以通过PKG_CONFIG_PATH环境变量指定pkg-config工具的检索目录。

而对应cgo来说,我们甚至可以通过PKG_CONFIG 环境变量可指定自定义的pkg-config程序。 如果是自己实现CGO专用的pkg-config程序,只要处理--cflags--libs两个参数即可。

下面的程序是macos系统下生成Python3的编译和链接参数:

// py3-config.go
func main() 
    for _, s := range os.Args 
        if s == "--cflags" 
            out, _ := exec.Command("python3-config", "--cflags").CombinedOutput()
            out = bytes.Replace(out, []byte("-arch"), []byte, -1)
            out = bytes.Replace(out, []byte("i386"), []byte, -1)
            out = bytes.Replace(out, []byte("x86_64"), []byte, -1)
            fmt.Print(string(out))
            return
        
        if s == "--libs" 
            out, _ := exec.Command("python3-config", "--ldflags").CombinedOutput()
            fmt.Print(string(out))
            return
        
    

然后通过以下命令构建并使用自定义的pkg-config工具:

$ go build -o py3-config py3-config.go
$ PKG_CONFIG=./py3-config go build -buildmode=c-shared -o gopkg.so main.go

go get 链

在使用go get获取Go语言包的同时会获取包依赖的包。比如A包依赖B包,B包依赖C包,C包依赖D包: pkgA -> pkgB -> pkgC -> pkgD -> ...。再go get获取A包之后会依次线获取BCD包。 如果在获取B包之后构建失败,那么将导致链条的断裂,从而导致A包的构建失败。

链条断裂的原因有很多,其中常见的原因有:

不支持某些系统, 编译失败
依赖 cgo, 用户没有安装 gcc
依赖 cgo, 但是依赖的库没有安装
依赖 pkg-config, windows 上没有安装
依赖 pkg-config, 没有找到对应的 bc 文件
依赖 自定义的 pkg-config, 需要额外的配置
依赖 swig, 用户没有安装 swig, 或版本不对

仔细分析可以发现,失败的原因中和CGO相关的问题占了绝大多数。这并不是偶然现象, 自动化构建C/C++代码一直是一个世界难题,到目前位置也没有出现一个大家认可的统一的C/C++管理工具。

因为用了cgo,比如gcc等构建工具是必须安装的,同时尽量要做到对主流系统的支持。 如果依赖的C/C++包比较小并且有源代码的前提下,可以优先选择从代码构建。

多个非main包中导出C函数

官方文档说明导出的Go函数要放main包,但是真实情况是其它包的Go导出函数也是有效的。 因为导出后的Go函数就可以当作C函数使用,所以必须有效。但是不同包导出的Go函数将在同一个全局的名字空间,因此需要小心避免重名的问题。 如果是从不同的包导出Go函数到C语言空间,那么cgo自动生成的_cgo_export.h文件将无法包含全部到处的函数声明, 我们必须通过手写头文件的方式什么导出的全部函数。

深入学习cgo(代码片段)

...CAPICGO调用在goruntime层面的处理CGO的静态/动态库封装以及编译链接参数CGO定位内存泄露CGO性能CGO最佳使用场景总结参考文献:很多场景下我们希望在Go中利用好已有的C/C&# 查看详情

cgo静态库和动态库(代码片段)

...O在使用C/C++资源的时候一般有三种形式:直接使用源码;链接静态库;链接动态库。直接使用源码就是在import"C"之前的注释部分包含C代码,或者在当前包中包含C/C++源文件。链接静态库和动态库的方式比较类似,都是通... 查看详情

golang交叉编译(代码片段)

golang的交叉编译要保证golang版本在1.5以上,本解决方案实例代码1.9版本执行的。GOOS=linuxGOARCH=amd64gobuildhello.go这里用到了两个变量:GOOS:目标操作系统GOARCH:目标操作系统的架构OSARCHOSVersionlinux386/amd64/arm>=Linux2.6darwin386/amd64OSX(Sn... 查看详情

深入学习cgo(代码片段)

...CAPICGO调用在goruntime层面的处理CGO的静态/动态库封装以及编译链接参数CGO定位内存泄露CGO性能CGO最佳使用场景总结参考文献:很多场景下我们希望在Go中利用好已有的C/C++库,Go语言通过自带的一个叫CGO的工具来支持C... 查看详情

golangcgo使用总结(代码片段)

...码可以直接以源代码形式提供或者打包静态库或动态库在编译时链接。推荐使用静态库的方式,这样方便代码隔离,编译的二进制也没有动态库依赖方便 查看详情

环境篇golang环境变量二三事

...for‘gobuild-compiler=gccgo‘.表示"gobuild-compiler=gccgo",编译时指定的-compiler参数。ARThecommandtousetomanipulatelibraryarchiveswhenbuildingwiththegccgocompiler.Thedefaultis‘ar‘.打包工具,默认"ar"。CCThecommandtousetocompileCcode.编译C语言的工... 查看详情

golang如何交叉编译(代码片段)

Golang支持交叉编译,即在一个平台上生成另一个平台的可执行程序。方法如下:Mac下编译Linux和Windows64位可执行程序CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.goCGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.goLinux下编译Mac和Windows64位可执行程序C... 查看详情

golang对不同系统的编译(代码片段)

Golang支持在一个平台下生成另一个平台可执行程序的交叉编译功能。Mac下编译#mac编译linux执行文件CGO_ENABLED=0GOOS=linuxGOARCH=amd64gobuildmain.go#mac编译windows执行文件CGO_ENABLED=0GOOS=windowsGOARCH=amd64gobuildmain.goLinux下编译#编译mac执行文件CGO_EN... 查看详情

cgo基础(代码片段)

CGO基础要使用CGO特性,需要安装C/C++构建工具链,在macOS和Linux下是要安装GCC,在windows下是需要安装MinGW工具。同时需要保证环境变量CGO_ENABLED被设置为1,这表示CGO是被启用的状态。在本地构建时CGO_ENABLED默认是启用的,当交叉构... 查看详情

c++(qt)软件调试---编译器及编译参数学习(代码片段)

C++(Qt)软件调试—编译器及编译参数学习(4)文章目录C++(Qt)软件调试---编译器及编译参数学习(4)1、前言1.1编译器参数有什么用1.2学C++软件调试为什么要先学编译器参数1.3C++程序异常调试... 查看详情

go-跨平台编译(代码片段)

跨平台编译默认我们gobuild的可执行文件都是当前操作系统可执行的文件,如果我想在windows下编译一个linux下可执行文件,那需要怎么做呢?只需要指定目标操作系统的平台和处理器架构即可:SETCGO_ENABLED=0//禁用CGOSETGOOS=linux//目... 查看详情

gitadd.和gitadd*区别(代码片段)

gitadd.会把本地所有untrack的文件都加入暂存区,并且会根据.gitignore做过滤;gitadd*会忽略.gitignore把任何文件都加入.一个.gitignore文件例子:*.o*.a*.so_obj_test*.[568vq][568vq].out*.cgo1.go*.cgo2.c_cgo_defun.c_cgo_gotypes.go_cgo_exp 查看详情

python调用cgo库——32位和64位问题

...了一个cgo库并使用python(带有ctypes包)来调用它。代码被编译成32位和64位版本,这些库分别被32位和64位python程序调用。我发现显然参数没有正确传递。我认为这可能与数组的定义和在python程序和库之 查看详情

如何构建 CGO 程序静态链接到 glibc 和动态链接到 libGL.so?

】如何构建CGO程序静态链接到glibc和动态链接到libGL.so?【英文标题】:HowtobuildCGOprogramstaticlinkingtoglibcanddynamiclinkingtolibGL.so?【发布时间】:2022-01-2304:50:26【问题描述】:我的操作系统是Kali,运行GLIBC_2.32。我需要为运行GLIBC_2.28的... 查看详情

cgo类型转换(代码片段)

类型转换最初CGO是为了达到方便从Go语言函数调用C语言函数(用C语言实现Go语言声明的函数)以复用C语言资源这一目的而出现的(因为C语言还会涉及回调函数,自然也会涉及到从C语言函数调用Go语言函数(用Go语言实现C语言声... 查看详情

cgo内部机制(代码片段)

...录并且在构建完成时保留中间文件。如果是比较简单的cgo代码我们也可以直接通过手工调用gotoolcgo命令来查看生成的中间文件。在一个Go源文件中,如果出现了import"C"指令则表示将调用cgo命令生成对应的 查看详情

编译链接实战添加头文件路径和库路径(代码片段)

...I参数指定了,比如头文件放在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,如果不加你会得到一个"xxxx.h:Nosuchfileordirectory"的错误。-I参数可以用相对路径,比如头文件在当前目录,可以用-I.... 查看详情

gcc编译过程与动态链接库和静态链接库(代码片段)

...的是.lib.dlllinux上对应的是.a.so在这里先介绍下Linux下的gcc编译的几个选项g++-chellospeak.cpp会将hellospeak.cpp选项-c用来告诉编译器编译源代码但不要执行链接,输出结果为对象文件。文件默认名与源码文件名相同,只是将其后缀变为.o... 查看详情