【问题标题】:Cross compiling "Hello World" on Mac for Android在 Mac 上为 Android 交叉编译“Hello World”
【发布时间】:2016-08-15 02:10:14
【问题描述】:

我正在尝试构建一个标准的“Hello, World!” Android 的命令行可执行文件。可执行文件将通过adb shell 运行。

0。 Go (Golang) 源码

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, world!")
}

1A。构建命令

$ CGO_ENABLED=0 GOOS=android GOARCH=arm GOARM=7 go build .

1B。输出(重新排列换行符以防止滚动条)

# github.com/asukakenji/cross
warning: unable to find runtime/cgo.a
/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
ld: warning: ignoring file
    /var/folders/dd/6k6vkzbd6d5803xj9zkjdhmh0000gn/T/go-link-150305609/go.o,
    file was built for unsupported file format
    ( 0x7F 0x45 0x4C 0x46 0x01 0x01 0x01 0x00
      0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 )
    which is not the architecture being linked (x86_64):
    /var/folders/dd/6k6vkzbd6d5803xj9zkjdhmh0000gn/T/go-link-150305609/go.o
Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

1℃。再次构建命令

以下命令给出相同的结果:

$ env CGO_ENABLED=0 GOOS=android GOARCH=arm GOARM=7 go build .

2。构建命令(详细)

我尝试过使用"-v",如下所述:

$ CGO_ENABLED=0 GOOS=android GOARCH=arm GOARM=7 go build \
      -x -ldflags "-extldflags -v" .

它给了我超过 100 行的消息,所以我不会在这里发布,除非有必要。 go build 命令似乎尝试使用与Xcode 捆绑在一起的clang 编译源代码。

3A。构建命令(成功,但是...)

鉴于发现错误编译器的提示,我尝试像这样设置$CC

$ CGO_ENABLED=0 GOOS=android GOARCH=arm GOARM=7 \
      CC=/path/to/arm-linux-androideabi/bin/clang go build .

arm-linux-androideabimake_standalone_toolchain.py(或make-standalone-toolchain.sh)的输出。

3B。输出

可执行文件(名为cross)已成功构建,并带有以下消息:

# github.com/asukakenji/cross
warning: unable to find runtime/cgo.a

我试过adb push它并在Android上用adb shell运行它,它运行良好。

我的问题

  1. 为什么需要 C 编译器? Go 不是开箱即用的交叉编译吗?
  2. 在为 Linux(而不是 Android)构建时,编译工作正常:

    $ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .
    

    为什么?

  3. go build 命令一直在寻找runtime/cgo.a,即使我没有在源代码中使用 CGO,即使我设置了CGO_ENABLED=0。我怎样才能摆脱警告?没有一个有什么害处?

【问题讨论】:

  • 为了方便起见,我认为您需要 Android NDK。 clang 也应该可以,但它不适用于 Android 原生编程,可能需要大量配置。
  • 如上所述,我已经使用 Android NDK 成功编译了代码。问题是为什么需要它。当我以“Linux amd64”为目标时,永远不需要交叉编译器。有什么区别?
  • 我猜想一些与 -D__ARM_ANDROID__ -DHAVE_PTHREADS 相关的东西,我们总是在 NDK 构建中添加。
  • 恐怕你没有抓住重点。我非常了解NDK可以完成工作。我只是想知道为什么需要 C 编译器。据我所知,除非使用 CGO,否则 Golang 编译器无需其他编译器即可将源代码编译为二进制文件。我已经在 Mac 上成功构建了这些,没有任何外部工具:“darwin amd64”(主机)、“linux 386”、“linux amd64”、“windows 386”、“windows amd64”。但是为什么“android arm”是个例外呢?重点不是使用的标志,重点是为什么首先需要 C 编译器!
  • 我知道你的意思。但是 C-things (compiler/stdlib/macros/headers/#pragma) 严重依赖平台。另一个 C 编译器几乎不能与 android C-things 一起工作。仍然是我的猜测,没有深入挖掘。

标签: android go cross-compiling cgo


【解决方案1】:

你需要使用Android NDK编译为android,你可以从链接下载它,或者从Android Studio

然后你可以在如下路径中找到编译器链接:

Last login: Fri Sep  4 09:25:16 on console

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
Hasans-Air:~ hajsf$ pwd
/Users/hajsf
Hasans-Air:~ hajsf$ cd Library
Hasans-Air:Library hajsf$ cd android
Hasans-Air:android hajsf$ cd sdk
Hasans-Air:sdk hajsf$ cd ndk
Hasans-Air:ndk hajsf$ ls
21.3.6528147
Hasans-Air:ndk hajsf$ cd 21.3.6528147
Hasans-Air:21.3.6528147 hajsf$ cd toolchains
Hasans-Air:toolchains hajsf$ cd llvm
Hasans-Air:llvm hajsf$ cd prebuilt
Hasans-Air:prebuilt hajsf$ ls
darwin-x86_64
Hasans-Air:prebuilt hajsf$ cd darwin-x86_64
Hasans-Air:darwin-x86_64 hajsf$ cd bin
Hasans-Air:bin hajsf$ pwd
/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin

然后你可以使用一个反射g:

  1. 您要为其构建 Android 应用程序的架构,例如 aarch64
  2. 你要编译的Android API,比如Android 30

你可以在那里看到可用选项的完整列表,你可以看到你想要的,比如aarch64-linux-android30-clang

如果您在 Windows 10,您可以在以下位置找到它:

"C:\Users\${user}\AppData\Local\Android\Sdk\ndk\${NKD_version}\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android30-clang"

有 4 个可用的链接器是:

//CC_FOR_TARGET=/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android30-clang
//CC_FOR_TARGET=/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/i686-linux-android30-clang
//CC_FOR_TARGET=/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/x86_64-linux-android30-clang
//CC_FOR_TARGET=/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi30-clang

之后只有你可以进行交叉编译,如:

$ CGO_ENABLED=1
$ GOOS=android
$ GOARCH=arm64
$ CC_FOR_TARGET=/Users/hajsf/Library/android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android30-clang
$ go build -buildmode=c-shared -o lib-aarch64-android30.so lib.go

一个简单的cgo 文件lib.go 可以是:

package main

import "C"
import "fmt"

//export HelloWorld
func HelloWorld() {
    fmt.Printf("hello world from GO\n")
}

//export GetKey
func GetKey() *C.char {
    theKey := "123-456-789"
    return C.CString(theKey)
}

func main() {}

如图所示,编译成功完成,lib-aarch64-android30.soib-aarch64-android30.h 均已生成,没有任何错误。

快速说明,不要偏离问题的范围,如果你返回一个字符串,那么如果你调用这个函数,必须在C代码中显式释放这个函数的返回值来自C 代码,但是当你从垃圾收集器环境中调用它时,Java/Kotlin 你不想担心它。

如果释放分配的缓冲区不方便,通常填充调用者提供的缓冲区:

func GetKey(buff *C.char, n int) int

如果可以分配内存但不想处理 C 字符串,可以将缓冲区插入指针并返回大小。

func GetKey(buff **C.char) int

【讨论】:

    【解决方案2】:

    Android 不是交叉编译的官方目标平台。如果您只需要命令行可执行文件,那么您可以设置 GOOS=linux 因为 android 是一个底层的 linux,否则请查看 https://github.com/golang/go/wiki/Mobile

    【讨论】:

      【解决方案3】:

      如果您运行该代码,您会发现 android 作为官方目标平台 列为 GOOS/GOARCH

      $go tool dist list
      

      【讨论】:

      • 当然,现在在 go1.10.3 中。这个问题是两年前的问题。
      【解决方案4】:

      cgo 要求可能是因为 go 需要 libc 在 Android 上进行 DNS 查找:https://github.com/golang/go/issues/8877

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-08
        • 2020-08-15
        • 2017-09-15
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多