1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-27 14:52:20 +00:00
advanced-go-programming-book/appendix/draft/ch2-xx-go-get-friendly.md
2018-07-22 18:57:42 +08:00

82 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 2.6. CGO包的组织(Doing)
凡事都有两面性CGO虽然是继承了C/C++庞大的生态资源同时也带来了C/C++语言的诸多问题。第一个遇到的重要问题是如何打包CGO中对用到的C/C++库或代码的依赖。很多用户对Go语言的第一映像是构建和运行都非常快速甚至可以当中一个脚本语言来使用。但是这种映像的前提是程序要能够正常构建如何正确构建一个使用了CGO特性的Go语言包对很多用户是一个挑战。
## 常见的C和C++编译问题
在真实世界中C/C++一般是混合存在的。编译这类C/C++混合代码遇到的问题也是CGO经常需要解决的问题。混合C/C++代码的构建和组织的有两个原则一是C/C++头文件最小化二是C/C++编译参数和头文件分离。
原本的C语言世界是简单的头文件也是简单的。当C++引入了函数重载一个函数名有多个实现后头文件也变得复杂起来。主要的原因是C++为了支持多个有着不同参数类型的同名函数在生成目标文件时要对应不同的链接符号。简言之C++中在编译阶段默认采用和C语言不同的名字修饰规则同时支持C语言采用的名字修饰规则。
因为cgo只支持C语言语法因此cgo也只能包含C语言的头文件。如果这个C语言头文件没有针对C++做过特殊的处理那么在被其它的C++代码包含时需要放到`extern "C" { ... }`括号中这是C++针对兼容C语言而增加的语法
编译C/C++源文件时,`.c`后缀名的对应是C语言代码其它的一般是C++代码。C和C++代码编译时有着不同的编译选项,在`#cgo`指令中CFLAGS对应C语言的编译选项CPPFLAGS对应C和C++共有的编译选项CXXFLAGS则对于C++特有的编译参数。C/C++源文件编译后成为一个个目标文件C/C++的目标文件没有区别共同使用LDFALAGS表示链接选项。
需要说明的是如果C++代码中使用了C++11或更新的特性需要在C++编译选项中指明,否则会导致编译错误。
<!-- 组织一个例子 -->
## 依赖二进制库
最简单的CGO程序是没有任何的依赖仅仅只是通过`import "C"`语句表示启用CGO特性
```go
// hello.go
package main
import "C"
func main() {
println("hello cgo")
}
```
这种程序最为简单,而且又是单个文件,我们可以通过`go run hello.go`命令来直接运行。但是这个程序虽然简单但是依然会出发cgo命令行工具依然会触发C语言代码的编译链接的过程。最终我们的得到的可执行程序会依赖一个底层的运行时库。
在不同的操作系统下看可执行程序有哪些依赖有着不同的工具。Linux系统是ldd命令macOS系统是otool命令Windows下则有带节目的Depends依赖检查工具。下面是这个例子在macOS系统下默认生成的可执行程序的依赖
```
$ go build -o a.out
$ otool -L a.out
a.out:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)
```
对于macOS系统`libSystem.B.dylib`一般会包含C语言运行时库的实现。对于Linux和Windows环境应该有类似的C语言运行时库。当然普通用户很少需要关心这些细节。
但是随着Docker等容器技术的流行如何打包一个最小化Go写的程序容器成了某些用户的追求。Go语言之所以会随着容器技术会大势流行这是一个非常重要的因素我们甚至在没有C语言运行时库的环境正常运行Go语言写的程序。下面是在Linux环境打包静态库版本的C语言运行时库
```
$ go build --ldflags '-w -s -extldflags "-static"' hello.go
```
如果现在再用Linux的ldd命令查看将不会有任何的依赖这是一个绝对绿色的程序。
更实用的CGO包一般会依赖第三方的C/C++库。如果本地操作系统中已经安装了依赖的第三方库那么这种情况就和依赖标准的C/C++库差不多了。但是在发布时需要确保运行程序的目标系统也包含一致的第三方C/C++共享库。
需要注意的是释放采用静态库版本的C/C++运行时库是最终构建用户的选择每个cgo包本书不应该过多设置最终的构建选项因为这可能导致不同cgo包之间的链接参数的冲突
<!-- pkg-config 自定义的命令 -->
## 同时打包C源码
## 打包巨量C源码的问题
TODO
<!--
可移植的cgo
小的cgo包直接内置c/c++代码
1. copy到本目录
2. 创建 include 文件
很大的c包全部内置会导致构建复杂化
分2步1 go generate 进行构建
2 lib 包含
-->