From d202d22407ddf7822a03d2c2a11e32f857e54be6 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Sun, 4 Feb 2024 18:39:23 +0330 Subject: [PATCH] Add internal build cli --- cmd/internal/build_libcore/main.go | 217 +++++++++++++++++++++++++++++ cmd/internal/build_shared/sdk.go | 99 +++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 cmd/internal/build_libcore/main.go create mode 100644 cmd/internal/build_shared/sdk.go diff --git a/cmd/internal/build_libcore/main.go b/cmd/internal/build_libcore/main.go new file mode 100644 index 0000000..f7b7f24 --- /dev/null +++ b/cmd/internal/build_libcore/main.go @@ -0,0 +1,217 @@ +package main + +import ( + "flag" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/hiddify/libcore/cmd/internal/build_shared" + _ "github.com/sagernet/gomobile" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing/common/rw" +) + +var target string + +func init() { + flag.StringVar(&target, "target", "android", "target platform") +} + +func main() { + flag.Parse() + + switch target { + case "windows": + buildWindows() + case "linux": + buildLinux() + case "macos": + buildMacOS() + case "android": + buildAndroid() + case "ios": + buildIOS() + } +} + +var ( + sharedFlags []string + sharedTags []string + iosTags []string +) + +const libName = "libcore" + +func init() { + sharedFlags = append(sharedFlags, "-trimpath") + sharedFlags = append(sharedFlags, "-ldflags", "-s -w") + sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_ech", "with_utls", "with_clash_api", "with_grpc") + iosTags = append(iosTags, "with_dhcp", "with_low_memory", "with_conntrack") +} + +func setDesktopEnv() { + os.Setenv("CGO_ENABLED", "1") + os.Setenv("buildmode", "c-shared") +} + +func buildWindows() { + setDesktopEnv() + os.Setenv("GOOS", "windows") + os.Setenv("GOARCH", "amd64") + os.Setenv("CC", "x86_64-w64-mingw32-gcc") + + args := []string{"build"} + args = append(args, sharedFlags...) + args = append(args, "-tags") + args = append(args, strings.Join(sharedTags, ",")) + + output := filepath.Join("bin", libName+".dll") + args = append(args, "-o", output, "./custom") + + command := exec.Command("go", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + log.Fatal(err) + } +} + +func buildLinux() { + setDesktopEnv() + os.Setenv("GOOS", "linux") + os.Setenv("GOARCH", "amd64") + + args := []string{"build"} + args = append(args, sharedFlags...) + args = append(args, "-tags") + args = append(args, strings.Join(sharedTags, ",")) + + output := filepath.Join("bin", libName+".so") + args = append(args, "-o", output, "./custom") + + command := exec.Command("go", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + log.Fatal(err) + } +} + +func buildMacOS() { + libPaths := []string{} + for _, arch := range []string{"amd64", "arm64"} { + out, err := buildMacOSArch(arch) + if err != nil { + log.Fatal(err) + return + } + libPaths = append(libPaths, out) + } + + args := []string{"-create"} + args = append(args, libPaths...) + args = append(args, "-output", filepath.Join("bin", libName+".dylib")) + + command := exec.Command("lipo", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + log.Fatal(err) + } +} + +func buildMacOSArch(arch string) (string, error) { + setDesktopEnv() + os.Setenv("GOOS", "darwin") + os.Setenv("GOARCH", arch) + os.Setenv("CGO_CFLAGS", "-mmacosx-version-min=10.11") + os.Setenv("CGO_LDFLAGS", "-mmacosx-version-min=10.11") + + args := []string{"build"} + args = append(args, sharedFlags...) + tags := append(sharedTags, iosTags...) + args = append(args, "-tags") + args = append(args, strings.Join(tags, ",")) + + filename := libName + "-" + arch + ".dylib" + output := filepath.Join("bin", filename) + args = append(args, "-o", output, "./custom") + + command := exec.Command("go", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + return "", err + } + return output, nil +} + +func buildAndroid() { + build_shared.FindMobile() + build_shared.FindSDK() + + args := []string{ + "bind", + "-v", + "-androidapi", "21", + "-javapkg=io.nekohasekai", + "-libname=box", + "-target=android", + } + + args = append(args, sharedFlags...) + args = append(args, "-tags") + args = append(args, strings.Join(sharedTags, ",")) + + output := filepath.Join("bin", libName+".aar") + args = append(args, "-o", output, "github.com/sagernet/sing-box/experimental/libbox", "./mobile") + + command := exec.Command(build_shared.GoBinPath+"/gomobile", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + log.Fatal(err) + } +} + +func buildIOS() { + build_shared.FindMobile() + + args := []string{ + "bind", + "-v", + "-libname=box", + "-target", "ios,iossimulator,tvos,tvossimulator,macos", + } + + args = append(args, sharedFlags...) + tags := append(sharedTags, iosTags...) + args = append(args, "-tags") + args = append(args, strings.Join(tags, ",")) + + output := filepath.Join("bin", "Libcore.xcframework") + args = append(args, "-o", output, "github.com/sagernet/sing-box/experimental/libbox", "./mobile") + + command := exec.Command(build_shared.GoBinPath+"/gomobile", args...) + command.Stdout = os.Stdout + command.Stderr = os.Stderr + log.Debug("command: ", command.String()) + err := command.Run() + if err != nil { + log.Fatal(err) + } + + rw.CopyFile("Info.plist", filepath.Join(output, "Info.plist")) +} diff --git a/cmd/internal/build_shared/sdk.go b/cmd/internal/build_shared/sdk.go new file mode 100644 index 0000000..5d651e5 --- /dev/null +++ b/cmd/internal/build_shared/sdk.go @@ -0,0 +1,99 @@ +package build_shared + +import ( + "go/build" + "os" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/rw" +) + +var ( + androidSDKPath string + androidNDKPath string +) + +func FindSDK() { + searchPath := []string{ + "$ANDROID_HOME", + "$HOME/Android/Sdk", + "$HOME/.local/lib/android/sdk", + "$HOME/Library/Android/sdk", + } + for _, path := range searchPath { + path = os.ExpandEnv(path) + if rw.FileExists(path + "/licenses/android-sdk-license") { + androidSDKPath = path + break + } + } + if androidSDKPath == "" { + log.Fatal("android SDK not found") + } + if !findNDK() { + log.Fatal("android NDK not found") + } + + os.Setenv("ANDROID_HOME", androidSDKPath) + os.Setenv("ANDROID_SDK_HOME", androidSDKPath) + os.Setenv("ANDROID_NDK_HOME", androidNDKPath) + os.Setenv("NDK", androidNDKPath) + os.Setenv("PATH", os.Getenv("PATH")+":"+filepath.Join(androidNDKPath, "toolchains", "llvm", "prebuilt", runtime.GOOS+"-x86_64", "bin")) +} + +func findNDK() bool { + if rw.FileExists(androidSDKPath + "/ndk/26.1.10909125") { + androidNDKPath = androidSDKPath + "/ndk/26.1.10909125" + return true + } + ndkVersions, err := os.ReadDir(androidSDKPath + "/ndk") + if err != nil { + return false + } + versionNames := common.Map(ndkVersions, os.DirEntry.Name) + if len(versionNames) == 0 { + return false + } + sort.Slice(versionNames, func(i, j int) bool { + iVersions := strings.Split(versionNames[i], ".") + jVersions := strings.Split(versionNames[j], ".") + for k := 0; k < len(iVersions) && k < len(jVersions); k++ { + iVersion, _ := strconv.Atoi(iVersions[k]) + jVersion, _ := strconv.Atoi(jVersions[k]) + if iVersion != jVersion { + return iVersion > jVersion + } + } + return true + }) + for _, versionName := range versionNames { + if rw.FileExists(androidSDKPath + "/ndk/" + versionName) { + androidNDKPath = androidSDKPath + "/ndk/" + versionName + return true + } + } + return false +} + +var GoBinPath string + +func FindMobile() { + goBin := filepath.Join(build.Default.GOPATH, "bin") + + if runtime.GOOS == "windows" { + if !rw.FileExists(goBin + "/" + "gobind.exe") { + log.Fatal("missing gomobile.exe installation") + } + } else { + if !rw.FileExists(goBin + "/" + "gobind") { + log.Fatal("missing gomobile installation") + } + } + GoBinPath = goBin +}