@Awille
2019-01-12T12:25:38.000000Z
字数 7130
阅读 105
交叉编译 go
交叉编译指的是在一个平台上生成另一个平台的可执行代码
在当前编译平台下,编译本平台运行的可执行程序。
在当前的编译平台之下,编译出于能在与本地平台体系架构不同的另一种目标平台上运行的程序
Golang下载地址
下载后缀为pkg的文件
安装后缀为pkg的文件以后,应该是直接帮助我们设置了环境变量的,我们直接就可以使用go命令了。
go env 可以查看go的环境变量。
GOARCH="amd64"GOBIN=""GOEXE=""GOHOSTARCH="amd64"GOHOSTOS="darwin"GOOS="darwin"GOPATH="/Users/will/go"GORACE=""GOROOT="/usr/local/go"......
这里的环境变量我自己不设置一下总是觉得很不顺畅,决定还是自己设置一下。
常规操作:
1、vim .bash_profile
2、在文件中添加以下脚本命令:
export GOPATH=/Users/will/goexport GOBIN=$GOPATH/binexport PATH=$PATH:$GOBIN
3、source .bash_profile
环境变量之后还能正常使用go命令,应该是没有冲突,那我们就那么使用,不管了。
安装完之后,我们会想有一个IDE来简化我们平时的编码过程,我看到网上有很多,什么sublime添加go语法插件之类,突然发现IntelliJ也有go插件,那么决定以后就使用IntelliJ这个IDE了。在mac环境下,选择intelliJ的preference -> plugin, 搜索go,下载go language插件。结束以后,我们就可以直接创建go工程项目了。
每次接触一个新语言,第一个要打的程序就是hello world,这里我们也搞一个。
在IntelliJ中创建Go 工程项目。
若要将go编译成一个可执行程序,那么package必须是main,我们就先在项目下创建一个main包,并在下面创建一个go文件
package mainimport "fmt"func main() {fmt.Println("Hello World!")}
运行结果:
Hello World!
可以在vsCode当中加入go插件,用vsCode来编辑。
mac go 插件安装指导
依照上面的指导安装各种插件结束以后,打开vsCode,弹出提示更新之类的按照提示走就好了。
创建go 文件,添加以下代码:
package mainimport ("fmt")func main() {fmt.Println("Hello World!")}
在命令行中执行该程序:
admindeMacBook-Pro:GoProjects will$ go run HelloWorld.go
Hello World!
如果想将代码编译成一个可执行程序,那么package必须是main
如果表编译成库,那么package没有限制。
go中所有的代码都应该隶属于一个包
定义:var 变量名 变量类型
赋值:变量名 = 值
定义变量并初始化:变量名:=值
定义的变量必须得被用到,否则会报错
函数定义:
func 函数名(参数1 参数1类型,参数2 参数2类型) 返回值类型 {
}
包 变量 函数 例:
package mainimport ("fmt")func main() {fmt.Println("Hello World!")var number intnumber = 1fmt.Println(number)fmt.Println(add(1, 2))}func add(a int, b int) int {return a + b}
内存自动回收,new分配内存以后不需要手动释放。
goroute,轻量级线程,可以创建成千上万个goroute
基于CSP模型实现:CSP模型介绍
并发demo,只需在函数前面加一个 go 修饰即可实现并发:
package mainimport ("fmt""time")func main() {fmt.Println("Hello World!")for i:=0; i < 5; i ++ {go add(i, i + 1)}}func add(a int, b int){time.Sleep(time.Second)fmt.Println(a + b)}
运行之后发现打印结果只有HelloWord, 这里是因为主程序main执行结束以后不会等待线程运行结束,直接退出了
我们在main中加一个延迟:
time.Sleep(6 * time.Second)
运行结果:
Hello World!
7
3
5
1
9
可以看到这个并发的过程。
定义管道:
//定义管道 第一个参数chan表明这是一个管道// 第二个参数表明管道传输的数据类型// 第三个参数表明管道容量pipe := make(chan int, 3)
从管道中放入值:
pipe <- value
从管道中取值:
variable <- pipe
管道测试:
package mainimport ("fmt""time")var pipe = make(chan int, 3)func main() {go thread1()time.Sleep(time.Second)go therad2()time.Sleep(time.Second)}func thread1() {pipe <- 1}func therad2() {a:= <- pipefmt.Println(a)}
go在1.5版本以后改进了对交叉编译的支持,1.5版本以后,go进行跨平台交叉编译变得相当简单,只需要设置两个环境变量GOODS和GOARCH
环境变量:
GOODS:你的应用程序将要运行平台的操作系统。
GOARCH:你的应用程序将要运行平台的处理器架构。
有效组合:
$GOOS $GOARCHandroid armdarwin 386darwin amd64darwin armdarwin arm64dragonfly amd64freebsd 386freebsd amd64freebsd armlinux 386linux amd64linux armlinux arm64linux ppc64linux ppc64lelinux mipslinux mipslelinux mips64linux mips64lenetbsd 386netbsd amd64netbsd armopenbsd 386openbsd amd64openbsd armplan9 386plan9 amd64solaris amd64windows 386windows amd64
首先设置GOODS和GOARCH两个环境变量,接着执行go build
示例:
env GOOS=linux GOARCH=arm go build 文件
生成指定可执行文件的名字:
env GOOS=linux GOARCH=arm go build -o test.bin
go有一个go mobile工具直接生成.aar文件供java代码调用。
go get golang.org/x/mobile/cmd/gomobilegomobile init
因为编译.aar文件是需要用到android 的 ndk的。
如果没有配置ndk,会有下面的报错:
gomobile: no Android NDK path is set. Please run gomobile init with the ndk-bundle installed through the Android SDK manager or with the -ndk flag set.
照着提示去做就好了
gomobile init -ndk "/Users/will/Library/Android/sdk/android-ndk-r15c"
package goAndroidTestfunc ShowResponseFromGo(request string) string {return "I have reciecved requst: " + request + ", I will process it"}
这里如果你的函数名是小写会生成出错,想被外部引用必须大写
在go文件目录下:
gomobile bind -target=android
这里需要注意啊,你一定要把这个编译的文件放在你设置的Gopath的src路径之下,否则你编译会出错的,比如下面的报错:
admindeMacBook-Pro:goAndroidTest will$ gomobile bind -target=android
gomobile: go build -buildmode=c-shared -o=/var/folders/zl/c9s1pvdd78j6n692z0gskpd00000gp/T/gomobile-work-045200192/android/src/main/jniLibs/armeabi-v7a/libgojni.so gobind failed: exit status 1
/var/folders/zl/c9s1pvdd78j6n692z0gskpd00000gp/T/gomobile-work-045200192/src/gobind/go_goAndroidTestmain.go:17:2: local import "." in non-local package
在这个报错当中,我们可以看到这个信息:
go build -buildmode=c-shared -o=/var/folders/zl/c9s1pvdd78j6n692z0gskpd00000gp/T/gomobile-work-045200192/android/src/main/jniLibs/armeabi-v7a/libgojni.so
可以看到这里其实也是生成armeabi-v7a架构下的so文件,我们不用gomobie的bind命令,直接生成so文件也可以的:
正常运行之后,我们可以看到生成的aar文件。
在我们需要使用的模块当中,将aar放入lib文件夹以后,添加依赖
android字段中添加:
repositories {flatDir {dirs 'libs'}}
dependence中添加:
implementation(name: 'goAndroidTest', ext: 'aar')
导入后,make一下工程,可以看到在external Library上生成了java文件:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package goAndroidTest;import go.Seq;public abstract class GoAndroidTest {private GoAndroidTest() {}public static void touch() {}private static native void _init();public static native String showResponseFromGo(String var0);static {Seq.touch();_init();}}
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("Awille", GoAndroidTest.showResponseFromGo("i come frome android"));
}
}
输出:
com.example.gotest E/Awille: I have reciecved requst: i come frome android, I will process it
整体看下来这比不管是用ndk还是用Cmake去生成so库流程上都简单很多。我们不用编写mk或者cmakeList文件,直接生成jar包或者aar,直接调用就可以了。
还是在之前那个包目录下:
go build -buildmode=c-shared -o=libgojni.so
查看生成的文件:
admindeMacBook-Pro:goAndroidTest will$ lsandroidTest.go goAndroidTest.aargoAndroidTest-sources.jar libgojni.so
可以看到除了之前生成的jar跟aar文件,还生成了刚刚我们指定生成的so库。
目前看下我们的环境变量:
GOARCH="amd64"GOOS="darwin"
这个darwin操作系统,处理区为amd64架构似乎不能再android中使用,我们重新生成以下so库,配置目标操作系统。
env GOOS=android GOARCH=arm go build -buildmode=c-shared -o=libgojni.so
这里我们生成android系统 arm架构下可以使用的so库。
下面尝试使用这个so库,添加至相应文件中:
static {System.loadLibrary("gojni");//参数为so库名称}
定义该方法:
public static native String ShowResponseFromGo(String request);
调用:
Log.e("Awille", MainActivity.ShowResponseFromGo("Hi, how are you"));
查看报错:
java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.example.gotest.MainActivity.ShowResponseFromGo(java.lang.String) (tried Java_com_example_gotest_MainActivity_ShowResponseFromGo and Java_com_example_gotest_MainActivity_ShowResponseFromGo__Ljava_lang_String_2)
说是没有实现,看来在go语言中我们之前写的函数不符合jni的调用规范。
上一节生成的so库无法使用,查了一些博客,做了一些常识:
修改go代码:
package goAndroidTestimport "C"//export ShowResponseFromGofunc ShowResponseFromGo(request string) string {return "I have reciecved requst: " + request + ", I will process it"}
improt "C"//export ShowResponseFromGo
这里注释的//export ShowResponseFromGo 是必要的,用来做函数的声明
之前用下面的命令,指定GOOS和GOARCH来生成so库,总是会报错 can't load package,这是加载不到import的C库导致的,找这个错误提示的意思,应该是要把C库的整个源代码放在我这个go文件的目录之下,感觉这应该是错误的打包方式,我尝试了各种更换目录,总是不行:
env GOOS=android GOARCH=arm go build -buildmode=c-shared -o=libgojni.so
后来去掉了指定的GOOS跟GOARCH的参数,发现就可以了,但不知道这个生成的so库在java中能不能调用,先尝试下:
新的编译命令:
go build -buildmode=c-shared -o=libgojni.so
尝试了下,依然是crash了,提示的错误同样是:
先去下载罪行的JNA依赖():
JNA依赖下载链接
jna链接so库:
21:30:23.291 9264-9264/com.example.gotest E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.gotest, PID: 9264
java.lang.UnsatisfiedLinkError: Unable to load library 'gojni':
dlopen failed: can't find ARM symbol
dlopen failed: can't find ARM symbol
dlopen failed: "/data/app/com.example.gotest-1/lib/arm/libgojni.so" has bad ELF magic
我特么的就是怀疑生成的so库不对应arm架构,妈的傻逼,妈的傻逼