新增调试信息

This commit is contained in:
2025-10-27 22:15:25 +08:00
parent ae62457d8c
commit 04642cb2f0
5479 changed files with 683397 additions and 3450 deletions
+188
View File
@@ -0,0 +1,188 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/hiddify/hiddify-core/config"
pb "github.com/hiddify/hiddify-core/hiddifyrpc"
v2 "github.com/hiddify/hiddify-core/v2"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/spf13/cobra"
)
var (
hiddifySettingPath string
configPath string
defaultConfigs config.HiddifyOptions = *config.DefaultHiddifyOptions()
commandBuildOutputPath string
)
var commandBuild = &cobra.Command{
Use: "build",
Short: "Build configuration",
Run: func(cmd *cobra.Command, args []string) {
err := build(configPath, hiddifySettingPath)
if err != nil {
log.Fatal(err)
}
},
}
var generateConfig = &cobra.Command{
Use: "gen",
Short: "gen configuration",
Run: func(cmd *cobra.Command, args []string) {
conf, err := v2.GenerateConfig(&pb.GenerateConfigRequest{
Path: args[0],
})
if err != nil {
log.Fatal(err)
}
log.Debug(string(conf.ConfigContent))
},
}
var commandCheck = &cobra.Command{
Use: "check",
Short: "Check configuration",
Run: func(cmd *cobra.Command, args []string) {
err := check(configPath)
if err != nil {
log.Fatal(err)
}
},
}
func init() {
commandBuild.Flags().StringVarP(&commandBuildOutputPath, "output", "o", "", "write result to file path instead of stdout")
addHConfigFlags(commandBuild)
mainCommand.AddCommand(commandBuild)
mainCommand.AddCommand(generateConfig)
}
func build(path string, optionsPath string) error {
if workingDir != "" {
path = filepath.Join(workingDir, path)
if optionsPath != "" {
optionsPath = filepath.Join(workingDir, optionsPath)
}
os.Chdir(workingDir)
}
options, err := readConfigAt(path)
if err != nil {
return err
}
HiddifyOptions := &defaultConfigs // config.DefaultHiddifyOptions()
if optionsPath != "" {
HiddifyOptions, err = readHiddifyOptionsAt(optionsPath)
if err != nil {
return err
}
}
config, err := config.BuildConfigJson(*HiddifyOptions, *options)
if err != nil {
return err
}
if commandBuildOutputPath != "" {
outputPath, _ := filepath.Abs(filepath.Join(workingDir, commandBuildOutputPath))
err = os.WriteFile(outputPath, []byte(config), 0o644)
if err != nil {
return err
}
fmt.Println("result successfully written to ", outputPath)
// libbox.Setup(outputPath, workingDir, workingDir, true)
// instance, err := NewService(*patchedOptions)
} else {
os.Stdout.WriteString(config)
}
return nil
}
func check(path string) error {
content, err := os.ReadFile(path)
if err != nil {
return err
}
return libbox.CheckConfig(string(content))
}
func readConfigAt(path string) (*option.Options, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var options option.Options
err = options.UnmarshalJSON(content)
if err != nil {
return nil, err
}
return &options, nil
}
func readConfigBytes(content []byte) (*option.Options, error) {
var options option.Options
err := options.UnmarshalJSON(content)
if err != nil {
return nil, err
}
return &options, nil
}
func readHiddifyOptionsAt(path string) (*config.HiddifyOptions, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var options config.HiddifyOptions
err = json.Unmarshal(content, &options)
if err != nil {
return nil, err
}
if options.Warp.WireguardConfigStr != "" {
err := json.Unmarshal([]byte(options.Warp.WireguardConfigStr), &options.Warp.WireguardConfig)
if err != nil {
return nil, err
}
}
if options.Warp2.WireguardConfigStr != "" {
err := json.Unmarshal([]byte(options.Warp2.WireguardConfigStr), &options.Warp2.WireguardConfig)
if err != nil {
return nil, err
}
}
return &options, nil
}
func addHConfigFlags(commandRun *cobra.Command) {
commandRun.Flags().StringVarP(&configPath, "config", "c", "", "proxy config path or url")
commandRun.MarkFlagRequired("config")
commandRun.Flags().StringVarP(&hiddifySettingPath, "hiddify", "d", "", "Hiddify Setting JSON Path")
commandRun.Flags().BoolVar(&defaultConfigs.EnableFullConfig, "full-config", false, "allows including tags other than output")
commandRun.Flags().StringVar(&defaultConfigs.LogLevel, "log", "warn", "log level")
commandRun.Flags().BoolVar(&defaultConfigs.InboundOptions.EnableTun, "tun", false, "Enable Tun")
commandRun.Flags().BoolVar(&defaultConfigs.InboundOptions.EnableTunService, "tun-service", false, "Enable Tun Service")
commandRun.Flags().BoolVar(&defaultConfigs.InboundOptions.SetSystemProxy, "system-proxy", false, "Enable System Proxy")
commandRun.Flags().Uint16Var(&defaultConfigs.InboundOptions.MixedPort, "in-proxy-port", 2334, "Input Mixed Port")
commandRun.Flags().BoolVar(&defaultConfigs.TLSTricks.EnableFragment, "fragment", false, "Enable Fragment")
commandRun.Flags().StringVar(&defaultConfigs.TLSTricks.FragmentSize, "fragment-size", "2-4", "FragmentSize")
commandRun.Flags().StringVar(&defaultConfigs.TLSTricks.FragmentSleep, "fragment-sleep", "2-4", "FragmentSleep")
commandRun.Flags().BoolVar(&defaultConfigs.TLSTricks.EnablePadding, "padding", false, "Enable Padding")
commandRun.Flags().StringVar(&defaultConfigs.TLSTricks.PaddingSize, "padding-size", "1300-1400", "PaddingSize")
commandRun.Flags().BoolVar(&defaultConfigs.TLSTricks.MixedSNICase, "mixed-sni-case", false, "MixedSNICase")
commandRun.Flags().StringVar(&defaultConfigs.RemoteDnsAddress, "dns-remote", "1.1.1.1", "RemoteDNS (1.1.1.1, https://1.1.1.1/dns-query)")
commandRun.Flags().StringVar(&defaultConfigs.DirectDnsAddress, "dns-direct", "1.1.1.1", "DirectDNS (1.1.1.1, https://1.1.1.1/dns-query)")
commandRun.Flags().StringVar(&defaultConfigs.ClashApiSecret, "web-secret", "", "Web Server Secret")
commandRun.Flags().Uint16Var(&defaultConfigs.ClashApiPort, "web-port", 6756, "Web Server Port")
}
+21
View File
@@ -0,0 +1,21 @@
package cmd
import (
_ "github.com/hiddify/hiddify-core/extension/repository"
"github.com/hiddify/hiddify-core/extension/server"
"github.com/spf13/cobra"
)
var commandExtension = &cobra.Command{
Use: "extension",
Short: "extension configuration",
Args: cobra.MaximumNArgs(0),
Run: func(cmd *cobra.Command, args []string) {
server.StartTestExtensionServer()
},
}
func init() {
// commandWarp.Flags().StringVarP(&warpKey, "key", "k", "", "warp key")
mainCommand.AddCommand(commandExtension)
}
+21
View File
@@ -0,0 +1,21 @@
package cmd
import (
"os"
"github.com/hiddify/hiddify-core/utils"
"github.com/spf13/cobra"
)
var commandGenerateCertification = &cobra.Command{
Use: "gen-cert",
Short: "Generate certification for web server",
Run: func(cmd *cobra.Command, args []string) {
err := os.MkdirAll("cert", 0o644)
if err != nil {
panic("Error: " + err.Error())
}
utils.GenerateCertificate("cert/server-cert.pem", "cert/server-key.pem", true, true)
utils.GenerateCertificate("cert/client-cert.pem", "cert/client-key.pem", false, true)
},
}
+59
View File
@@ -0,0 +1,59 @@
package cmd
import (
"os"
"os/signal"
"syscall"
v2 "github.com/hiddify/hiddify-core/v2"
"github.com/sagernet/sing-box/log"
"github.com/spf13/cobra"
)
var commandInstance = &cobra.Command{
Use: "instance",
Short: "instance",
Args: cobra.OnlyValidArgs,
Run: func(cmd *cobra.Command, args []string) {
hiddifySetting := defaultConfigs
if hiddifySettingPath != "" {
hiddifySetting2, err := v2.ReadHiddifyOptionsAt(hiddifySettingPath)
if err != nil {
log.Fatal(err)
}
hiddifySetting = *hiddifySetting2
}
instance, err := v2.RunInstanceString(&hiddifySetting, configPath)
if err != nil {
log.Fatal(err)
}
defer instance.Close()
ping, err := instance.PingAverage("http://cp.cloudflare.com", 4)
if err != nil {
// log.Fatal(err)
}
log.Info("Average Ping to Cloudflare : ", ping, "\n")
for i := 1; i <= 4; i++ {
ping, err := instance.PingCloudflare()
if err != nil {
log.Warn(i, " Error ", err, "\n")
} else {
log.Info(i, " Ping time: ", ping, " ms\n")
}
}
log.Info("Instance is running on port socks5://127.0.0.1:", instance.ListenPort, "\n")
log.Info("Press Ctrl+C to exit\n")
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
log.Info("CTRL+C recived-->stopping\n")
instance.Close()
},
}
func init() {
mainCommand.AddCommand(commandInstance)
addHConfigFlags(commandInstance)
}
+52
View File
@@ -0,0 +1,52 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"github.com/hiddify/hiddify-core/config"
"github.com/sagernet/sing-box/log"
"github.com/spf13/cobra"
)
var commandParseOutputPath string
var commandParse = &cobra.Command{
Use: "parse",
Short: "Parse configuration",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
err := parse(args[0])
if err != nil {
log.Fatal(err)
}
},
}
func init() {
commandParse.Flags().StringVarP(&commandParseOutputPath, "output", "o", "", "write result to file path instead of stdout")
mainCommand.AddCommand(commandParse)
}
func parse(path string) error {
if workingDir != "" {
path = filepath.Join(workingDir, path)
}
config, err := config.ParseConfig(path, true)
if err != nil {
return err
}
if commandParseOutputPath != "" {
outputPath, _ := filepath.Abs(filepath.Join(workingDir, commandParseOutputPath))
err = os.WriteFile(outputPath, config, 0644)
if err != nil {
return err
}
fmt.Println("result successfully written to ", outputPath)
} else {
os.Stdout.Write(config)
}
return nil
}
+28
View File
@@ -0,0 +1,28 @@
package cmd
import (
v2 "github.com/hiddify/hiddify-core/v2"
"github.com/spf13/cobra"
)
var commandRun = &cobra.Command{
Use: "run",
Short: "run",
Args: cobra.OnlyValidArgs,
Run: runCommand,
}
func init() {
// commandRun.PersistentFlags().BoolP("help", "", false, "help for this command")
// commandRun.Flags().StringVarP(&hiddifySettingPath, "hiddify", "d", "", "Hiddify Setting JSON Path")
addHConfigFlags(commandRun)
mainCommand.AddCommand(commandRun)
}
func runCommand(cmd *cobra.Command, args []string) {
v2.Setup("./tmp", "./", "./tmp", 0, false)
v2.RunStandalone(hiddifySettingPath, configPath, defaultConfigs)
}
+141
View File
@@ -0,0 +1,141 @@
package cmd
// import (
// "context"
// "fmt"
// "io"
// "math/rand"
// "net/http"
// "net/netip"
// "time"
// "github.com/hiddify/hiddify-core/common"
// // "github.com/hiddify/hiddify-core/extension_repository/cleanip_scanner"
// "github.com/spf13/cobra"
// "golang.org/x/net/proxy"
// )
// var commandTemp = &cobra.Command{
// Use: "temp",
// Short: "temp",
// Args: cobra.MaximumNArgs(2),
// Run: func(cmd *cobra.Command, args []string) {
// // fmt.Printf("Ping time: %d ms\n", Ping())
// scanner := cleanip_scanner.NewScannerEngine(&cleanip_scanner.ScannerOptions{
// UseIPv4: true,
// UseIPv6: common.CanConnectIPv6(),
// MaxDesirableRTT: 500 * time.Millisecond,
// IPQueueSize: 4,
// IPQueueTTL: 10 * time.Second,
// ConcurrentPings: 10,
// // MaxDesirableIPs: e.count,
// CidrList: cleanip_scanner.DefaultCFRanges(),
// PingFunc: func(ip netip.Addr) (cleanip_scanner.IPInfo, error) {
// fmt.Printf("Ping: %s\n", ip.String())
// return cleanip_scanner.IPInfo{
// AddrPort: netip.AddrPortFrom(ip, 80),
// RTT: time.Duration(rand.Intn(1000)),
// CreatedAt: time.Now(),
// }, nil
// },
// },
// )
// ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
// defer cancel()
// scanner.Run(ctx)
// t := time.NewTicker(1 * time.Second)
// defer t.Stop()
// for {
// ipList := scanner.GetAvailableIPs(false)
// if len(ipList) > 1 {
// // e.result = ""
// for i := 0; i < 2; i++ {
// // result = append(result, ipList[i])
// // e.result = e.result + ipList[i].AddrPort.String() + "\n"
// fmt.Printf("%d %s\n", ipList[i].RTT, ipList[i].AddrPort.String())
// }
// return
// }
// select {
// case <-ctx.Done():
// // Context is done
// return
// case <-t.C:
// // Prevent the loop from spinning too fast
// continue
// }
// }
// },
// }
// func init() {
// mainCommand.AddCommand(commandTemp)
// }
// func GetContent(url string) (string, error) {
// return ContentFromURL("GET", url, 10*time.Second)
// }
// func ContentFromURL(method string, url string, timeout time.Duration) (string, error) {
// if method == "" {
// return "", fmt.Errorf("empty method")
// }
// if url == "" {
// return "", fmt.Errorf("empty url")
// }
// req, err := http.NewRequest(method, url, nil)
// if err != nil {
// return "", err
// }
// dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:12334", nil, proxy.Direct)
// if err != nil {
// return "", err
// }
// transport := &http.Transport{
// Dial: dialer.Dial,
// }
// client := &http.Client{
// Transport: transport,
// Timeout: timeout,
// }
// resp, err := client.Do(req)
// if err != nil {
// return "", err
// }
// defer resp.Body.Close()
// if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
// return "", fmt.Errorf("request failed with status code: %d", resp.StatusCode)
// }
// body, err := io.ReadAll(resp.Body)
// if err != nil {
// return "", err
// }
// if body == nil {
// return "", fmt.Errorf("empty body")
// }
// return string(body), nil
// }
// func Ping() int {
// startTime := time.Now()
// _, err := ContentFromURL("HEAD", "https://cp.cloudflare.com", 4*time.Second)
// if err != nil {
// return -1
// }
// duration := time.Since(startTime)
// return int(duration.Milliseconds())
// }
+40
View File
@@ -0,0 +1,40 @@
package cmd
import (
"fmt"
"time"
"github.com/hiddify/hiddify-core/config"
v2 "github.com/hiddify/hiddify-core/v2"
"github.com/spf13/cobra"
)
var commandService = &cobra.Command{
Use: "tunnel run/start/stop/install/uninstall/activate/deactivate/exit",
Short: "Tunnel Service run/start/stop/install/uninstall/activate/deactivate/exit",
ValidArgs: []string{"run", "start", "stop", "install", "uninstall", "activate", "deactivate", "exit"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
arg := args[0]
switch arg {
case "activate":
config.ActivateTunnelService(config.HiddifyOptions{
InboundOptions: config.InboundOptions{
EnableTunService: true,
MixedPort: 12334,
TUNStack: "gvisor",
},
})
<-time.After(1 * time.Second)
case "deactivate":
config.DeactivateTunnelServiceForce()
case "exit":
config.ExitTunnelService()
default:
code, out := v2.StartTunnelService(arg)
fmt.Printf("exitCode:%d msg=%s", code, out)
}
},
}
+126
View File
@@ -0,0 +1,126 @@
package cmd
import (
"bufio"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/hiddify/hiddify-core/config"
T "github.com/sagernet/sing-box/option"
"github.com/spf13/cobra"
)
var warpKey string
var commandWarp = &cobra.Command{
Use: "warp",
Short: "warp configuration",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
out, err := generateWarp()
fmt.Printf("out=%v Error! %v", out, err)
if err != nil {
fmt.Printf("Error! %v", err)
}
},
}
func init() {
// commandWarp.Flags().StringVarP(&warpKey, "key", "k", "", "warp key")
mainCommand.AddCommand(commandWarp)
}
type WireGuardConfig struct {
Interface InterfaceConfig `json:"Interface"`
Peer PeerConfig `json:"Peer"`
}
type InterfaceConfig struct {
PrivateKey string `json:"PrivateKey"`
DNS string `json:"DNS"`
Address []string `json:"Address"`
}
type PeerConfig struct {
PublicKey string `json:"PublicKey"`
AllowedIPs []string `json:"AllowedIPs"`
Endpoint string `json:"Endpoint"`
}
type SingboxConfig struct {
Type string `json:"type"`
Tag string `json:"tag"`
Server string `json:"server"`
ServerPort int `json:"server_port"`
LocalAddress []string `json:"local_address"`
PrivateKey string `json:"private_key"`
PeerPublicKey string `json:"peer_public_key"`
Reserved []int `json:"reserved"`
MTU int `json:"mtu"`
}
func readWireGuardConfig(filePath string) (WireGuardConfig, error) {
file, err := os.Open(filePath)
if err != nil {
return WireGuardConfig{}, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var wgConfig WireGuardConfig
var currentSection string
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
currentSection = strings.TrimSpace(line[1 : len(line)-1])
continue
}
if currentSection == "Interface" {
parseInterfaceConfig(&wgConfig.Interface, line)
} else if currentSection == "Peer" {
parsePeerConfig(&wgConfig.Peer, line)
}
}
return wgConfig, nil
}
func parseInterfaceConfig(interfaceConfig *InterfaceConfig, line string) {
if strings.HasPrefix(line, "PrivateKey") {
interfaceConfig.PrivateKey = strings.TrimSpace(strings.SplitN(line, "=", 2)[1])
} else if strings.HasPrefix(line, "DNS") {
interfaceConfig.DNS = strings.TrimSpace(strings.SplitN(line, "=", 2)[1])
} else if strings.HasPrefix(line, "Address") {
interfaceConfig.Address = append(interfaceConfig.Address, strings.TrimSpace(strings.SplitN(line, "=", 2)[1]))
}
}
func parsePeerConfig(peerConfig *PeerConfig, line string) {
if strings.HasPrefix(line, "PublicKey") {
peerConfig.PublicKey = strings.TrimSpace(strings.SplitN(line, "=", 2)[1])
} else if strings.HasPrefix(line, "AllowedIPs") {
peerConfig.AllowedIPs = append(peerConfig.AllowedIPs, strings.TrimSpace(strings.SplitN(line, "=", 2)[1]))
} else if strings.HasPrefix(line, "Endpoint") {
peerConfig.Endpoint = strings.TrimSpace(strings.SplitN(line, "=", 2)[1])
}
}
func generateWarp() (*T.Outbound, error) {
_, _, wg, err := config.GenerateWarpInfo("", "", "")
// fmt.Printf("%v", wgConfig)
singboxConfig, err := config.GenerateWarpSingbox(*wg, "", 0, "", "", "", "")
singboxJSON, err := json.MarshalIndent(singboxConfig, "", " ")
if err != nil {
fmt.Println("Error marshaling Singbox configuration:", err)
return nil, err
}
fmt.Println(string(singboxJSON))
return nil, nil
}
+55
View File
@@ -0,0 +1,55 @@
package cmd
import (
"os"
"time"
"context"
"github.com/sagernet/sing-box/log"
"github.com/spf13/cobra"
)
var (
workingDir string
disableColor bool
)
var mainCommand = &cobra.Command{
Use: "HiddifyCli",
PersistentPreRun: preRun,
}
func init() {
mainCommand.AddCommand(commandService)
mainCommand.AddCommand(commandGenerateCertification)
mainCommand.PersistentFlags().StringVarP(&workingDir, "directory", "D", "", "set working directory")
mainCommand.PersistentFlags().BoolVarP(&disableColor, "disable-color", "", false, "disable color output")
}
func ParseCli(args []string) error {
mainCommand.SetArgs(args)
err := mainCommand.Execute()
if err != nil {
log.Fatal(err)
}
return err
}
func preRun(cmd *cobra.Command, args []string) {
if disableColor {
log.SetStdLogger(log.NewDefaultFactory(context.Background(), log.Formatter{BaseTime: time.Now(), DisableColors: true}, os.Stderr, "", nil, false).Logger())
}
if workingDir != "" {
_, err := os.Stat(workingDir)
if err != nil {
os.MkdirAll(workingDir, 0o0644)
}
if err := os.Chdir(workingDir); err != nil {
log.Fatal(err)
}
}
}
+217
View File
@@ -0,0 +1,217 @@
package main
import (
"flag"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/hiddify/hiddify-core/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"))
}
+99
View File
@@ -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
}