https://golang.org/cmd/cgo/
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo #cgo directives within these comments to tweak the behavior of the C, C++ or Fortran compiler. Values defined in multiple directives are concatenated together. The directive can include a list of build constraints limiting its effect to systems satisfying one of the constraints (see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax). For example:
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include
import "C"
Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool using a '#cgo pkg-config:' directive followed by the package names. For example:
// #cgo pkg-config: png cairo
// #include
import "C"
https://www.kancloud.cn/runningday/learngo/284086
CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS,和LDFLAGS可以在注释区域中,使用#cgo伪指令来定义,以改变C,C++,或Fortran编译器的行为。被多个指令定义的值会被联系在一起。指令还可以包含一个构建约束的列表,将其对系统的影响限制为满足其中一个约束条件。可以查看https://golang.org/pkg/go/build/#hdr-Build_Constraints 了解约束语法的详情。例如:
// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include
import "C"
或者,CPPFLAGS和LDFLAGS也可通过pkg-config工具获取,使用#cgo pkg-config:指令,后面跟上包名即可。比如:
// #cgo pkg-config: png cairo
// #include
import "C"
默认的pkg-config工具可以通过设置PKG_CONFIG环境变量来改变。
当编译时,CGO_CFLAGS,CGO_CPPFLAGS,CGO_CXXFLAGS,CGO_FFLAGS和CGO_LDFLAGS这些环境变量都会从指令中提取出来,并加入到flags中。包特定的flags需要使用指令来设置,而不是通过环境变量,所以这些构建可以在未更改的环境中也能正常运行。
一个包中的所有cgo CPPFLAGS和CFLAGS指令会被连接起来,并用来编译包中的C文件。一个包中的所有CPPFLAGS和CXXFLAGS指令会被连接起来,并用来编译包中的C++文件。一个包中的所有CPPFLAGS和FFLAGS指令会被连接起来,并用来编译包中的Fortran文件。在这个程序中任何包内的所有LDFLAGS指令会被连接起来,并在链接时使用。所有的pkg-config指令会被连接起来,并同时发送给pkg-config,以添加到每个适当的命令行标志集中。
当cgo指令被转化【parse】时,任何出现${SRCDIR}字符串的地方,都会被替换为包含源文件的目录的绝对路径。这就允许预编译的静态库包含在包目录中,并能够正确的链接。例如,如果foo包在/go/src/foo目录下:
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
就会被展开为:
// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
心得: // #cgo LDFLAGS: 可用来链接静态库。-L指定静态库所在目录,-l指定静态库文件名,注意静态库文件名必须有lib前缀,但是这里不需要写,比如上面的-lfoo实际找的是libfoo.a文件
当Go tool发现一个或多个Go文件使用了特殊导入“C”包,它就会在目录中寻找其它非Go文件,并将其编译为Go包的一部分。任何.c, .s, 或者.S文件会被C编译器编译。任何.cc, .cpp, 或者.cxx文件会被C++编译器编译。任何.f, .F, .for或者.f90文件会被fortran编译器编译。任何.h, .hh, .hpp或者.hxx文件不会被分开编译,但是,如果这些头文件修改了,那么C和C++文件就会被重新编译。默认的C和C++编译器有可能会分别被CC和CXX环境变量改变,那些环境变量可能包含命令行选项。
cgo tool对于本地构建默认是启用的。而在交叉编译时默认是禁用的。你可以在运行go tool时,通过设置CGO_ENABLED环境变量来控制它:设为1表示启用cgo, 设为0关闭它。如果cgo启用,go tool将会设置构建约束“cgo”。
在交叉编译时,你必须为cgo指定一个C语言交叉编译器。你可以在使用make.bash构建工具链时,设置CC_FOR_TARGET环境变量来指定,或者是在你运行go tool时设置CC环境变量来指定。与此相似的还有作用于C++代码的CXX_FOR_TARGET和CXX环境变量。
My Go软件包包含一个.c文件,该文件使用需要特定CFLAGS集的库。在命令行中“去安装”我可以指定CGO_CFLAGS所需的标志,并且一切正常。不过,我想这样做是为了让某人可以“获取”我的包并在不传递任何额外的命令行参数的情况下构建它
Go包装系统是否提供了一个我可以放置这样的配置的地方,以指定在包装时总是需要的一些参数?
(我知道围棋源文件做#cgo CFLAGS:指令,但记得,在我的包我有一个.c的源代码文件,所以需要CGO_CFLAGS设置为整个构建过程)
CGO提取物你的#cgo CFLAGS:到构建期间的环境变量(通过“-x”标记到去构建)。如果你去安装-x你会发现它支持你的Go库中指定的cflags/ldflags。换句话说,它应该只是在Go文件中指定它们。
为了使用您的任何C函数,你仍然有一些CGO混入你的。去的文件,这些文件只是声明你的标志,例如:
test.go:
package main
import “fmt”
/*
#cgo CFLAGS: -DTEST
#include
extern void ACFunction();
*/
import "C"
//export AGoFunction
func AGoFunction() {
fmt.Println(“AGoFunction()”)
}
func main() {
C.ACFunction()
}
test.c的:
#include “_cgo_export.h”
void ACFunction() {
#ifdef TEST
printf(“ACFunction()\n”);
#endif
AGoFunction();
}
在同一目录下把这些都会让go build皮卡test.go定义的标志和建筑test.c.当他们申请
0
My Go package includes a .c file that uses a library which needs certain CFLAGS set. On the command line to “go install” I can specify CGO_CFLAGS with the needed flags, and everything works. However, I would like to make it so that someone can “go get” my package and build it without passing any extra command line arguments.
Does the Go packaging system provide a place where I could put some config like this, to specify some arguments that are always needed when go installing a package?
(I’m aware of doing #cgo CFLAGS: directives in Go source files, but recall that in my package I have a .c source file so need the CGO_CFLAGS setting to the overall build process)
cgo extracts your #cgo CFLAGS: to an environment variable during build (pass “-x” flag to go build). If you go install -x you see that it honors your cflags/ldflags specified in your Go library. In other words, it should work to just specify them in your Go files.
In order to use any of your C functions you still have to mix some cgo into your .go files, in those files just declare your flags, for example:
test.go:
package main
import “fmt”
/*
#cgo CFLAGS: -DTEST
#include
extern void ACFunction();
*/
import "C"
//export AGoFunction
func AGoFunction() {
fmt.Println(“AGoFunction()”)
}
func main() {
C.ACFunction()
}
test.c:
#include “_cgo_export.h”
void ACFunction() {
#ifdef TEST
printf(“ACFunction()\n”);
#endif
AGoFunction();
}
Putting these in the same directory will make go build pickup the flags defined in test.go and apply them when building test.c.
https://www.slowbirdgogogo.com/gocmd/cgo
My Go软件包包含一个.c文件,该文件使用需要某些CFLAGS集的库。在“go install”的命令行中,我可以使用所需的标志指定CGO_CFLAGS,一切正常。但是,我想这样做,以便有人可以“获取”我的包并构建它而不传递任何额外的命令行参数。
Go包装系统是否提供了我可以放置这样的配置的地方,以指定在go install包裹时总是需要的一些参数?
(我知道在Go源文件中执行#cgo CFLAGS:指令,但回想一下,在我的包中我有一个.c源文件,所以需要CGO_CFLAGS设置到整个构建过程)
2 个答案:
答案 0 :(得分:6)
cgo 在构建期间将 #cgo CFLAGS:提取到环境变量中(将“-x”标志传递给 go build )。如果您去安装-x ,您会看到它尊重您在Go库中指定的cflags / ldflags。换句话说,它应该只在Go文件中指定它们。
答案 1 :(得分:0)
为了使用你的任何C函数,你仍然需要将一些cgo混合到.go文件中,在这些文件中只需声明你的标志,例如:
https://github.com/golang/go/issues/23323
cgo支持创建调用C代码的Go包。
使用 cgo
编写前需要导入伪包 C。之后就可以引用诸如C.size_t之类的类型,C.stdout之类的变量或C.putchar之类的函数。
如果在 C 包导入之前有注释,则在编译包的C部分时,将该注释(称为前导 preamble)用作头部。
// #include
// #include
import "C"
前导码可以包含任何C代码,包括函数和变量声明和定义。这些可以从Go代码中引用,就好像它们在包 C 中定义的一样。 所有在前导中声明的命名都可以使用,即使它们是以小写字母开头。例外:前导码中的静态变量可能不会被Go代码引用;静态函数是可以的。
详细示例见 $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp,点击查看使用介绍:https://golang.org/doc/articles/c_go_cgo.html.
CFLAGS,CPPFLAGS,CXXFLAGS,FFLAGS和LDFLAGS可以用如下注释中的伪#cgo指令来定义,以调整C,C ++或Fortran编译器的行为。 在多个指令中定义的值被连接在一起。指令可以包含一个构建约束列表,限制其对满足其中一个约束的系统的影响。查看https://golang.org/pkg/go/build/#hdr-Build_Constraints以了解更多细节。
// #cgo CFLAGS: -DPNG_DEBUG=1 // #cgo amd64 386 CFLAGS: -DX86=1 // #cgo LDFLAGS: -lpng // #include <png.h> import "C"
CPPFLAGS 和 LDFLAGS 可以通过pkg-config工具使用’#cgo pkg-config:’指令获得,后跟包名。
// #cgo pkg-config: png cairo
// #include
import "C"
通过设置PKG_CONFIG环境变量,可以更改默认的pkg-config工具。
构建时,CGO_CFLAGS,CGO_CPPFLAGS,CGO_CXXFLAGS,CGO_FFLAGS和CGO_LDFLAGS环境变量将添加到从这些指令派生的标志中。应该使用指令非修改环境变量来设置特定于包的标志,以便编译可以在未修改的环境中正常工作。
一个包中的所有cgo CPPFLAGS和CFLAGS指令被连接起来,用来编译该包中的C文件。 包中的所有CPPFLAGS和CXXFLAGS指令都被连接起来,用来编译该包中的C ++文件。 包中的所有CPPFLAGS和FFLAGS指令都被连接起来,用来编译该包中的Fortran文件。 程序中任何程序包中的所有LDFLAGS指令都在链接时连接在一起并使用。 所有的pkg-config指令都被连接在一起并同时发送到pkg-config,以添加到每个适当的命令行标志集。
在解析cgo指令时,任何出现的字符串${SRCDIR}都将被包含源文件目录的绝对路径替换。 这使得预编译的静态库被包含在软件包目录中并正确链接。例如,如果包foo位于/go/src/foo目录:
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
将被解析为
// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
当Go工具发现一个或多个Go文件使用特殊导入“C”时,它将在目录中查找其他非Go文件并将它们编译为Go包的一部分。 任何.c,.s或.s文件都将用C编译器编译。任何.cc,.cpp或.cxx文件都将用C++编译器编译。 任何.f,.F,.for或.f90文件将与fortran编译器一起编译。 任何.h,.hh,.hpp或.hxx文件将不会被单独编译,但如果这些头文件被更改,C和C++文件将被重新编译。 默认的C和C++编译器可以分别由CC和CXX环境变量进行更改——这些环境变量可能包含命令行选项。
cgo工具默认情况下启用本机构建。交叉编译默认是禁用的。 可以在运行go工具时设置CGO_ENABLED环境变量来控制:置为1以启用cgo,使用0禁用。 如果启用cgo,则go工具将使用“cgo”的构建约束。
在交叉编译时,必须指定一个供Cgo使用的C交叉编译器。 可以通过在使用make.bash构建工具链时设置CC_FOR_TARGET环境变量, 或者在运行go工具时设置CC环境变量来执行此操作。 CXX_FOR_TARGET和CXX环境变量对于C++工作方式类似。
Go引用C
在Go文件中,如果C的结构字段名称是Go中的关键字,可以通过用下划线作为前缀来访问: 例如x指向名为“type”的字段的C结构,则x._type访问该字段。 在Go中无法表述的C结构字段,比如位字段或未对齐的数据,在Go中被省略,用适当的填充代替,以达到下一个字段或结构的结尾。原文是:C struct fields that cannot be expressed in Go, such as bit fields or misaligned data, are omitted in the Go struct, replaced by appropriate padding to reach the next field or the end of the struct.
以下标准的C数字类型是可用的:C.char,C.schar(signed char),C.uchar(unsigned char),C.short,C.ushort(unsigned short),C.int,C.uint (unsigned short),C.long,C.ulong(unsigned long),C.longlong(long long),C.longlong(unsigned long long),C.float,C.double,C.complexfloat(complex float) C.complexdouble(complex double)。 C的void *是由Go的不安全的指针表示的。 C类型__int128_t和__int128_t由[16]byte类型表示。
要直接访问struct,union或enum类型,请使用struct_,union_或enum_作为前缀,如C.struct_stat。
任何C类型的T的大小都可以用C.sizeof_T,如C.sizeof_struct_stat。
由于Go不支持C的联合类型,所以C的联合类型被表示为具有相同长度的Go字节数组。
Go结构不能嵌入C类型的字段。
Go代码不能引用在非空C结构中出现的零大小的字段。要获得这样一个字段的地址(这是唯一的操作,你可以建立一个零大小的属性),你必须采取结构的地址,并添加结构的大小。
Cgo将C类型转换为等价的未导出的Go类型。由于转换是未导出的,Go包不应在其导出的API中公开C类型:一个Go包中使用的C类型与另一个中使用的C类型不同(即使类型名称相同)。原文是:Cgo translates C types into equivalent unexported Go types. Because the translations are unexported, a Go package should not expose C types in its exported API: a C type used in one Go package is different from the same C type used in another.
任何C函数(甚至是void函数)都可以在多个赋值语境中被调用来检索返回值(如果有)和C errno变量作为错误(如果函数返回void,则使用_来跳过结果值)。
n, err = C.sqrt(-1)
_, err := C.voidFunc()
var n, err = C.sqrt(1)
目前不支持调用C函数指针,但是可以声明包含C函数指针的Go变量,并将它们在Go和C之间来回传递。C代码可以调用从Go接收的函数指针。
package main
// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
// return f();
// }
//
// int fortytwo()
// {
// return 42;
// }
import “C”
import “fmt”
func main() {
f := C.intFunc(C.fortytwo)
fmt.Println(int(C.bridge_int_func(f)))
// Output: 42
}
在C中,函数参数写成一个固定大小的数组实际上需要一个指向数组的第一个元素的指针。C编译器知道这个调用约定,并相应地调用调用,但是Go不行。在Go中,必须显式地将指针传递给第一个元素:C.f(&C.x[0])。
一些特殊的功能通过复制数据在Go和C类型之间进行转换。
// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller’s responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char
// Go []byte slice to C array
// The C array is allocated in the C heap using malloc.
// It is the caller’s responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CBytes([]byte) unsafe.Pointer
// C string to Go string
func C.GoString(*C.char) string
// C data with explicit length to Go string
func C.GoStringN(*C.char, C.int) string
// C data with explicit length to Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte
作为一个特殊情况,C.malloc不直接调用C库malloc,而是调用一个包装C库malloc的Go助手函数,但保证永不返回nil。如果C的malloc指示内存不足,那么助手函数就会使程序崩溃,就像Go本身耗尽内存一样。由于C.malloc无法失败,所以不会返回errno的两个结果形式。
C调用Go
Go函数可以通过以下方式导出供C代码使用:
//export MyFunction
func MyFunction(arg1, arg2 int, arg3 string) int64 {…}
//export MyFunction2
func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {…}
它们转换成这样的C代码:
extern int64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
以上转换代码,可以在cgo输入文件复制的任何前导后,由_cgo_export.h生成的头文件中找到。具有多个返回值的函数被转换为返回结构——并非所有的Go类型都可以映射到C类型。
在文件中使用// export会对前导码造成限制:因为它被复制到两个不同的C输出文件中,所以它不能包含任何定义,只能包含声明。果一个文件同时包含定义和声明,那么这两个输出文件将产生重复的符号,链接器将失败。为避免这种情况,必须将定义放在其他文件的前导码或C源文件中。
传递指针
Go是带有GC的语言,垃圾收集器需要知道Go内存的每个指针的位置。因此,在Go和C之间传递指针是有限制的。
在本节中,Go指针是指由Go分配的内存指针(例如通过使用&运算符或调用预定义的新函数),并且术语C指针指向由C分配的内存的指针(例如通过调用C.malloc)。一个指针是一个Go指针还是一个C指针是一个动态属性,由内存如何分配决定;它与指针的类型无关。
Go代码可以将Go指针传递给C,只要它指向的Go内存不包含任何Go指针。C代码必须保留这个属性:它不能在Go内存中存储任何Go指针,即使是暂时的。
将指针传递给结构中的字段时,所涉及的Go内存是该字段占用的内存,而不是整个结构。将指针传递给数组或片段中的元素时,所涉及的Go内存是整个数组或整个片段的后备数组。
C代码在调用返回后可能不保留Go指针的副本。
由C代码调用的Go函数可能不会返回Go指针。 由C代码调用的Go函数可能将C指针作为参数,并可能通过这些指针存储非指针或C指针数据,但是它可能不会将Go指针存储在由C指针指向的内存中。 C代码调用的Go函数可能会将Go指针作为参数,但它必须保留指向的Go内存不包含任何Go指针的属性。
Go代码可能不会在C内存中存储Go指针。C代码可能会在C内存中存储Go指针,这取决于上面的规则:当C函数返回时,它必须停止存储Go指针。
这些规则在运行时动态检查。 检查由GODEBUG环境变量的cgocheck设置控制。 默认设置是GODEBUG=cgocheck= 1,它实现合理的动态检查且开销较小。这些检查可能完全禁用使用GODEBUG=cgocheck=0。 通过GODEBUG=cgocheck=2可以完成指针处理的完整检查,但运行时间有一定的代价。
通过使用 unsafe 包可以禁止这种强制执行,当然没有什么能阻止C代码做任何事情。但是,违反这些规则的程序可能会以意想不到的方式失败。
直接使用 cgo
go tool cgo [cgo options] [– compiler options] gofiles…
cgo将指定的输入Go源文件转换为多个输出Go和C的源文件。
当调用C编译器来编译包的C部分时,编译器选项是通过未解释的方式传递的。
当直接运行cgo时,有以下选项:
-dynimport file
写入由文件导入的符号列表。写入-dynout参数或标准输出。编译cgo包时会执行go build。
-dynout file
将 -dynimport 输出到文件。
-dynpackage package
为-dynimport输出设置Go包。
-dynlinker
将动态链接器写入-dynimport输出的一部分。
-godefs
用Go语法替换C包名。用于在系统调用软件包时生成文件以进行引导。
-srcdir directory
在命令行中列出目录下的Go输出文件。
-objdir directory
将所有生成的文件放在目录中。
-importpath string
Go包的导入路径。可选; 用于在生成的文件中更好的注释。
-exportheader file
如果有导出的函数,写入生成导出声明到文件中。
C代码可以使用#include来查看声明。.
-gccgo
为gccgo编译器生成输出,而不是 gc编译器。
-gccgoprefix prefix
用于gccgo的-fgo-prefix选项。
-gccgopkgpath path
用于gccgo的-fgo-pkgpath选项。
-import_runtime_cgo
设置后(默认)会在生成的输出中导入 runtime/cgo。
-import_syscall
设置后(默认)会在生成的输出中导入 syscall。
-debug-define
调试选项。打印#defines。
-debug-gcc
调试选项。跟踪C编译器的执行和输出。