使用Golang编译Android应用的完整指南

使用Golang编译Android应用的完整指南 我编写了下面的 build.go 来自动化交叉编译过程,但在 Android 上失败了,可能是什么原因:

package main

import (
	"os"
	"os/exec"
)

type compiles struct {
	sys  string
	arch string
}

func main() {
	var output string
	// go tool dist list
	systems := []compiles{
		compiles{
			sys:  "darwin",
			arch: "amd64",
		},
		compiles{
			sys:  "android",
			arch: "amd64",
		},
		compiles{
			sys:  "linux",
			arch: "amd64",
		},
	}

	for _, s := range systems {
		os.Setenv("GOOS", s.sys)
		os.Setenv("GOARCH", s.arch)
		switch println(s.sys); s.sys {
		case "darwin":
			output = "output.dmg"
		case "android", "linux":
			output = "output"
		default:
			println("Undefined")
		}
		cmd := exec.Command("go", "build", "-o", output, "main.go")
		err := cmd.Run()
		if err != nil {
			println("cmd.Run() failed with %s\n", err)
		}
	}
}

我得到了以下输出:

C:\Users\hasan\Documents\GoPlay\Env>go run build.go
darwin
android
cmd.Run() failed with %s
 (0xd9d6e0,0xc0000960e0)
linux

更多关于使用Golang编译Android应用的完整指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

内置的 println 函数已被弃用,不应再使用。应改用 fmt.Println。在你的例子中,由于使用了格式化指令,请使用 fmt.Printf 将:

println("cmd.Run() failed with %s\n", err)

改为:

fmt.Printf("cmd.Run() failed with %v\n", err)

在我的电脑上,这给了我以下信息:

> go run .\main.go
darwin
android
cmd.Run() failed with exit status 2
linux

要获取更多信息,请检查命令的 stdout 和 stderr:

cmd := exec.Command("go", "build", "-o", output, "main.go")
out, err := cmd.CombinedOutput()
if err != nil {
	fmt.Printf("cmd.Run() failed with %v:\n\noutput:\n\n%s\n", err, out)
}

这给了我以下输出:

> go run .\main.go
darwin
android
cmd.Run() failed with exit status 2:

output:

# command-line-arguments
c:\go\pkg\tool\windows_amd64\link.exe: running gcc failed: exec: "gcc": executable file not found in %PATH%


linux

也许你遇到了类似的错误。

更多关于使用Golang编译Android应用的完整指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢您详细的解答。在我的情况下,我遇到了这个错误:

C:\Users\hasan\Documents\GoPlay\Env>go run build.go
darwin
android
cmd.Run() failed with exit status 2:

output:

# command-line-arguments
c:\go\pkg\tool\windows_amd64\link.exe: running gcc failed: exit status 1
gcc: error: unrecognized command line option '-rdynamic'

我在 macOS 上尝试了同样的操作,得到了:

cmd.Run() failed with exit status 2:

output:

# command-line-arguments
/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
ld: unknown option: -z
clang: error: linker command failed with exit code 1 (use -v to see invocation)

然后我在 Linux(Win10 上的 Ubuntu WSL)上尝试,得到了:

# command-line-arguments
/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/tmp/go-link-178415548/go.o:(.data+0x0): undefined reference to `x_cgo_callers'
/tmp/go-link-178415548/go.o:(.data+0x8): undefined reference to `x_cgo_init'
/tmp/go-link-178415548/go.o:(.data+0x10): undefined reference to `x_cgo_mmap'
/tmp/go-link-178415548/go.o:(.data+0x18): undefined reference to `x_cgo_munmap'
/tmp/go-link-178415548/go.o:(.data+0x20): undefined reference to `x_cgo_notify_runtime_init_done'
/tmp/go-link-178415548/go.o:(.data+0x28): undefined reference to `x_cgo_sigaction'
/tmp/go-link-178415548/go.o:(.data+0x30): undefined reference to `x_cgo_thread_start'
/tmp/go-link-178415548/go.o:(.data+0x38): undefined reference to `x_cgo_setenv'
/tmp/go-link-178415548/go.o:(.data+0x40): undefined reference to `x_cgo_unsetenv'
/tmp/go-link-178415548/go.o:(.data+0x48): undefined reference to `_cgo_yield'
collect2: error: ld returned 1 exit status

你的 build.go 脚本在编译 Android 时失败,主要原因是 Android 平台需要特定的 C 编译器和链接器。以下是关键问题和解决方案:

问题分析

  1. 缺少 Android NDK 工具链:Android 编译需要 NDK 中的 C 编译器
  2. 环境变量设置不完整:需要设置 CCCXX 指向 Android 工具链
  3. Android/amd64 目标不常见:通常 Android 使用 ARM 架构

修正后的代码示例

package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

type compiles struct {
	sys   string
	arch  string
	cc    string // C 编译器路径
	cxx   string // C++ 编译器路径
	flags string // 额外编译标志
}

func main() {
	// 设置 Android NDK 路径(根据你的实际路径修改)
	ndkPath := "/path/to/android-ndk"
	
	systems := []compiles{
		{
			sys:  "darwin",
			arch: "amd64",
		},
		{
			sys:   "android",
			arch:  "arm64",
			cc:    filepath.Join(ndkPath, "toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang"),
			cxx:   filepath.Join(ndkPath, "toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++"),
			flags: "-ldflags=-extldflags=-pie",
		},
		{
			sys:  "linux",
			arch: "amd64",
		},
	}

	for _, s := range systems {
		fmt.Printf("编译 %s/%s...\n", s.sys, s.arch)
		
		// 设置环境变量
		os.Setenv("GOOS", s.sys)
		os.Setenv("GOARCH", s.arch)
		
		// 设置 C 编译器(Android 需要)
		if s.cc != "" {
			os.Setenv("CC", s.cc)
		}
		if s.cxx != "" {
			os.Setenv("CXX", s.cxx)
		}
		
		// 构建输出文件名
		output := fmt.Sprintf("output-%s-%s", s.sys, s.arch)
		if s.sys == "darwin" {
			output += ".dmg"
		}
		
		// 构建命令
		args := []string{"build", "-o", output}
		if s.flags != "" {
			args = append(args, s.flags)
		}
		args = append(args, "main.go")
		
		cmd := exec.Command("go", args...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		
		if err := cmd.Run(); err != nil {
			fmt.Printf("编译 %s/%s 失败: %v\n", s.sys, s.arch, err)
		} else {
			fmt.Printf("编译 %s/%s 成功\n", s.sys, s.arch)
		}
	}
}

完整的 Android 编译示例

// 专门编译 Android 的示例
func buildAndroid() error {
	// 设置 Android 编译环境
	os.Setenv("GOOS", "android")
	os.Setenv("GOARCH", "arm64")
	os.Setenv("GOARM", "7")
	
	// 设置 NDK 工具链(路径需要根据实际情况调整)
	ndkPath := os.Getenv("ANDROID_NDK_HOME")
	if ndkPath == "" {
		ndkPath = "/usr/local/android-ndk"
	}
	
	// 设置 C 编译器
	ccPath := filepath.Join(ndkPath, "toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang")
	os.Setenv("CC", ccPath)
	os.Setenv("CXX", ccPath+"++")
	
	// 构建命令
	cmd := exec.Command("go", "build", 
		"-o", "app-android-arm64",
		"-ldflags", "-s -w",
		"-buildmode=pie", // Android 需要 PIE
		"main.go")
	
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	
	return cmd.Run()
}

使用 gomobile 的替代方案

// 使用 gomobile 编译 Android APK
func buildWithGomobile() error {
	// 安装 gomobile
	exec.Command("go", "install", "golang.org/x/mobile/cmd/gomobile@latest").Run()
	exec.Command("gomobile", "init").Run()
	
	// 编译为 Android 库
	cmd := exec.Command("gomobile", "build",
		"-target", "android",
		"-androidapi", "21",
		"-o", "app.apk",
		".")
	
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	
	return cmd.Run()
}

关键注意事项

  1. 安装 Android NDK
# 下载并设置 NDK
export ANDROID_NDK_HOME=/path/to/android-ndk
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
  1. 验证工具链
func checkAndroidToolchain() error {
	cc := os.Getenv("CC")
	if cc == "" {
		return fmt.Errorf("CC 环境变量未设置")
	}
	
	// 检查编译器是否存在
	if _, err := os.Stat(cc); os.IsNotExist(err) {
		return fmt.Errorf("Android 编译器不存在: %s", cc)
	}
	
	return nil
}
  1. 交叉编译标志
// 完整的 Android 编译命令
cmd := exec.Command("go", "build",
	"-o", "android-app",
	"-ldflags", "-s -w -extldflags=-pie",
	"-buildmode=pie",
	"-tags", "android",
	"main.go")

主要问题是缺少 Android NDK 工具链和正确的环境变量配置。Android 编译需要特定的 C 编译器,并且建议使用 ARM 架构而非 amd64。

回到顶部